I have a Presenter
class which uses a field injected through Dagger, it looks something like this:
public class RssListPresenter {
@Inject
RssService rssService; // <-- injected field
public RssListPresenter() {
setupDI();
}
private void setupDI() {
DaggerNetworkComponent.builder()
.networkModule(new NetworkModule())
.build()
.inject(this);
}
public void loadItems() {
Rss rss = rssService.getRssFeed()
// ....
}
}
Everything works fine. Now, I would like to unit test the RssListPresenter
class. The question is how do I provide a mock RssService
to the presenter?
Ofcourse I can add a new method setRssService(RssService rssService)
to the presenter and use it to provide the mock from unit tests, but adding this method just for unit tests does not feel right. What would be the correct way to handle this?
For completeness here are the module and component declarations:
@Singleton
@Component(modules = NetworkModule.class)
public interface NetworkComponent {
void inject(RssListPresenter presenter);
}
@Module
public class NetworkModule {
@Provides
Retrofit provideRetrofit() {
// ...
}
@Provides
@Singleton
RssService providePcWorldRssService(Retrofit retrofit) {
return retrofit.create(RssService.class);
}
}
Property injection is like that is not so easy to test. In this case, Constructor injection is much better. Refactor your constructor to look like this:
private final RssService rssService;
@Inject
public RssListPresenter(RssService rssService) {
this.rssService = rssService;
}
Now you can test it easily:
//mocks
RssService mockRssService;
//system under test
RssListPresenter rssListPresenter;
@Before
public void setup() {
mockRssService = Mockito.mock(RssService.class);
rssListPresenter = new RssListPresenter(mockRssService);
}
You probably shouldn't be using DaggerNetworkComponent.inject(this)
inside RssListPresenter
. Instead you should be configuring dagger so that when it injects members into your top-level classes (Activity
, Fragment
, Service
) it can access the object graph and create an instance of your RssPresenter
.
Why only put injectors in Activity
and Service
and not in something like RssListPresenter
? These are classes that are instantiated by the Android system and so you have no choice but to use injectors.
To clarify, Activity
, Fragment
etc. are ideal injection targets. RssListPresenter
etc. are injected dependencies. You need to configure the dependency injection framework, dagger, so that it can provide the correct dependencies to inject into the injection targets.
So you will also need to write a @Provides
method for RssListPresenter
@Provides provideRssListPresenter(RssService rssService) {
return new RssListPresenteR(rssService);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With