2

I learn how to test the presenter layer of MVP architecture in android, my presenter using retrofit 2 and in my activity I used dagger 2 as dependency injection to my presenter, this is my Dagger and presenter injection looks like:

@Inject AddScreenPresenter addScreenPresenter; 

This is the Dagger builder :

DaggerAddScreenComponent.builder() .netComponent(((App) getApplicationContext()).getNetComponent()) .addScreenModule(new AddScreenModule(this, new ContactDatabaseHelper(this))) .build().inject(this); 

and this is my presenter constructor :

@Inject public AddScreenPresenter(Retrofit retrofit, AddScreenContact.View view, ContactDatabaseHelper contactDatabaseHelper) { this.retrofit = retrofit; this.view = view; this.contactDatabaseHelper = contactDatabaseHelper; } 

I have write the unit test class and mock the Retrofit class, but when I run it, the error appears :

Mockito cannot mock/spy following: 

- final classes - anonymous classes - primitive types

This is the test class :

@RunWith(MockitoJUnitRunner.class) public class AddScreenPresenterTest { private AddScreenPresenter mAddPresenter; @Mock private Retrofit mRetrofit; @Mock private Context mContext; @Mock private AddScreenContact.View mView; @Mock private ContactDatabaseHelper mContactDatabaseHelper; String firstName, phoneNumber; Upload upload; @Before public void setup() { mAddPresenter = new AddScreenPresenter(mRetrofit, mView, mContactDatabaseHelper); firstName = "aFirstName"; phoneNumber = "998012341234"; Uri path = Uri.parse("android.resource://"+BuildConfig.APPLICATION_ID+"/" + R.drawable.missing); upload = new Upload(); upload.title = firstName; upload.description = "aDescription"; upload.albumId = "XXXXX"; upload.image = new File(path.getPath()); } @Test public void checkValidationTest() { verify(mAddPresenter).checkValidation(firstName, phoneNumber); } @Test public void uploadMultiPartTest() { verify(mAddPresenter).uploadMultiPart(upload); } } 

this is my module :

@Module public class AddScreenModule { private final AddScreenContact.View mView; private final ContactDatabaseHelper mContactDatabaseHelper; public AddScreenModule (AddScreenContact.View view, ContactDatabaseHelper contactDatabaseHelper) { this.mView = view; this.mContactDatabaseHelper = contactDatabaseHelper; } @Provides @CustomScope AddScreenContact.View providesAddScreenContactView() { return mView; } @Provides @CustomScope ContactDatabaseHelper providesContactDatabaseHelper() { return mContactDatabaseHelper; } } 

I know that Retrofit class is a final class, and now I stuck and don't know how to create the presenter object in my test class. Please help me, how to create the object of the presenter class with retrofit in the constructor. Feel free to ask if my question is not clear enough, and thank you very much for your help.

2
  • Can you post the code for your dagger Module? Commented Dec 21, 2016 at 13:52
  • I have add my module for you @Zain Commented Dec 21, 2016 at 15:12

1 Answer 1

6

Personally I'd make the presenter not depend on the Retrofit class but rather on the services created by Retrofit - These are mockable.

It's hard to say from the code you posted which services your presenter actually uses, but for the sake of simplicity let's say it uses only one and let's say it's AddsService - This is an interface ready to work with Retrofit. Something like this for example

public interface AddsService { @GET(...) Call<List<Adds>> getAllAdds(); } 

Now you can make your presenter depend on this rather than Retrofit

@Inject public AddScreenPresenter(AddsService addsService, AddScreenContact.View view, ContactDatabaseHelper contactDatabaseHelper){ this.addsService = addsService; this.view = view; this.contactDatabaseHelper = contactDatabaseHelper; } 

You now need to provide this dependency. I'm guessing you have also a NetModule since you have a NetComponent, so I assume you can just do:

@Module public class NetModule { // Methods providing Retrofit @Provides @Singleton public AddsService providesAddsService(Retrofit retrofit) { return retrofit.create(AddsService.class); } } 

Notice how the providesAddsService depends on retrofit? This should be already provided since your presenter is depending on it. You shouldn't need to change anything for that. Dagger is able to figure out how to provide Retrofit to the method providesAddsService.

Please notice also that I'm assuming you can provide these in a Singleton scope. I assume this because in your code you retrieve the component from the application, which should handle the singleton scope.

Now in your tests you can simply mock AddsService and test your presenter.

If your presenter depends on more services, I'd also pass them in the constructor and provide the implementations with Dagger.

As a bonus, let me also say that the retrofit instance and the retrofit services should only be created once (or at least as less times as possible). This is because they're usually expensive operations and you usually always query the same endpoints with different parameters.

EDIT

To answer some of the questions in the comments. First the easy one: How to create the presenter in the test classes? Like you I too try to get away from Dagger during tests, that's why I prefer constructor dependency injection just like you show you're using. So in my test class I'd have something very similar like you:

@RunWith(MockitoJUnitRunner.class) public class AddScreenPresenterTest { private AddScreenPresenter mAddPresenter; @Mock private AddsService addsService; // ... @Before public void setUp() throws Exception { mAddPresenter = new AddScreenPresenter(addsService, mView, mContactDatabaseHelper); // ... } } 

So basically the only difference is that I would pass the mock to the service.

Now the second question: How to call the presenter constructor from the activity? Well you don't... that's the whole idea of dependency injection. You should use dagger to provide your presenter. I think this is already what you do and I guess this is what it's in your activity:

@Inject AddScreenPresenter addScreenPresenter; 

So all you need to do is have a provider method in your module that provides this and is able to inject it.

You can also make the component return the presenter provided by the module:

@Component(...) public interface AddScreenComponent { AddScreenPresenter getPresenter(); } 

And then in your activity you'd do something like:

addScreenPresenter = component.getPresenter(); 

I don't really have any preference here. The key point is to understand that you should not build the objects yourself (unless inside @Modules). As a rule of thumb any time you see new being used that means you have a tight dependency on that object and you should extract it to be injected. So this is why you should avoid creating the presenter inside your activity. It will couple the presenter to the activity.

Sign up to request clarification or add additional context in comments.

3 Comments

thanks for your help, can you please provide how to call the presenter construction in your example from the activity??
and please provide example how to create the presenter in the test class, @Fred . many thanks before
@user1290932 I've update my answer. Hope this helps.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.