4

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:

  • LoginComponent, which uses two modules: LoginModule and HTTPModule
  • LoginModule in one of its methods requires an OkHttp instance, which is provided by HTTPModule.

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!

Community
  • 1
  • 1
dexafree
  • 131
  • 3
  • 11
  • 1
    there is an article that was released a bit sooner about testing with dagger, maybe it could bring you some answers [link] https://medium.com/@fabioCollini/android-testing-using-dagger-2-mockito-and-a-custom-junit-rule-c8487ed01b56#.t0q5o7p7w – gropapa Feb 07 '16 at 21:18
  • Can't you override the `HTTPModule` at the creation of the component in the `Dagger__Component.builder()`'s method? – EpicPandaForce Feb 11 '16 at 09:35
  • @gropapa I will try this article, thanks! :) – dexafree Feb 12 '16 at 10:04
  • @EpicPandaForce The HTTPModule is a Module, and as I said, in Dagger 2 it looks like method override for Modules is not supported, that's the main problem :( – dexafree Feb 12 '16 at 10:04
  • 1
    You **can** override the module's provider methods, as long as you don't specify `@Provides` on the overridden method where you've overridden it. Like `new HTTPModule() { @Override public provideOkHttpClient() { return new MockHttpClient(); };` should work. – EpicPandaForce Feb 12 '16 at 10:47
  • @EpicPandaForce wow, I did not understand that. I will try and comment the result, thanks a lot! – dexafree Feb 13 '16 at 10:15

1 Answers1

2

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.

Itai Hanski
  • 8,540
  • 5
  • 45
  • 65
  • Thanks for the answer, but that's not exactly what I was asking for. I want to return an OkHttpClient in both cases, so the interface is not really needed here (unless you want to add yet another layer). The problem I'm having is in overriding the returned implementation on test time. – dexafree Feb 12 '16 at 10:03
  • 1
    "Yet another layer" is incorrect since you don't have a network layer. OkHttp is a specific class. By providing it as a dependency you are tightly coupling multiple classes of yours with the OkHttp library, which has its cost. Either way you can easily create a mock version of it using mockito and provide it to your classes when instantiating them for your testing directly instead of via a module – Itai Hanski Feb 14 '16 at 08:28