Reputation: 131
I'm currently developing an Android MVP Application, and I'm trying to separate my dependencies in different Dagger2 Modules.
The problem I'm having is about changing a module in Unit Test Time. The scenario is the following:
The code is the following:
@Singleton
@Component(modules = {LoginModule.class, HTTPModule.class})
public interface LoginComponent {
}
@Module(includes = {HTTPModule.class})
public class LoginModule {
@Provides
@Singleton
public MyThing provideMyThing(OkHttpClient client) {
// Do things with it
}
}
@Module
public class HTTPModule {
@Provides
@Singleton
public OkHttpClient provideOkHttpClient(){
// Return the OkHttpClient
}
}
The thing is, at test time I would need to change the OkHttpClient that is returned (by making it accept all the certificates, as when I run it on the JVM it does not accept the LetsEncrypt certificate).
Also I would need that because I need to declare that MyTest.class
can be injected with module, and as MyTest.class
is under the app/src/test/
folder, it's not visible for the classes that are placed under app/src/main/
. What I've done until now is to copy and paste the Component and the modules to the /test/
folder, and make the injected class declaration there. But I know there must be a proper way to achieve what I'm looking for.
Another thing I've tried is annotating the methods with custom Scopes (creating a @TestScope
annotation). However this leads me to the same problem that I had commented before: I cannot make the MyTest.class
visible to the component, because it's placed under the /test/
folder.
I've already checked other similar questions, such as this one and this another one, but this last one is for running tests with Robolectric, and by now I'm able to unit test most of my code with JUnit4 only (Android Studio 2-Beta 8).
If anyone could point me to the right direction, I would be more than grateful.
Thanks in advance!
Upvotes: 4
Views: 4281
Reputation: 8690
You're using Dependency injection in a way that still keeps your code tightly coupled. Generally speaking you want your dependencies to be interfaces instead of actual classes. This keeps your code nice and loose, easy to read, modify and maintain.
Hide your network operations behind an interface to allow you to modify the network implementation whenever you need to. This could be done for testing - in your case, but it will also allow you to switch out the network library if you'll want to or need to in the future without changing any other code.
Try something like this:
@Module
public class HTTPModule {
@Provides
@Singleton
public NetworkProvider provideNetworkProvider(){
// Return the Network provider
}
}
The network abstraction layer:
public interface NetworkProvider {
// Methods to send requests and receive async responses
}
The OkHttp implementation:
public class OkHttpNetworkProvider implements NetworkProvider {
// Implement NetworkProvider. This is the only class that
// knows about OkHttp and its components
}
Now you can create a mock version of NetworkProvider
and use it for testing, whether via a test module or directly.
Upvotes: 2