11

I used to work in MVP and I usually test my presenters using a plain Junit (Not the Instrumentation !) , since Presenters only have the business logic and no references to Android internals whatsoever.

Now by switching to Dagger 2 , I understood that I have a problem setting up a "TestModule" for my app component.

  1. Creating a component will not work from within a test class (probably because "apt" is not running there)
  2. Didn't find any examples for using Dagger with a standard Junit testing. Every example I have found only relies on Instrumentation testing or Roboelectric (which basically mocks Activities and other Android related stuff) , but this is just a UI testing for me , and I don't need that.

Just to make things clear , I am talking about the tests that are located at app->src->test folder not the app->src->androidTest !

So do I do something wrong ? Or missing something ? Can anyone explain or give examples on how to use Dagger 2 in normal unit tests ?

Ivelius
  • 4,953
  • 7
  • 30
  • 54

4 Answers4

7

I'm not sure if my solution will work for you but I see no reason it shouldn't. First I created testInjectionComponent

@Singleton
@Component(modules = {MockNetworkModule.class})
public interface MockInjectionComponent extends InjectionComponent {
void inject(DaggerUnitTest daggerUnitTest);
}

Then my Unit Tests I add injection in the before method. like so:

@Before
public void setUp() throws Exception {
    MockInjectionComponent mockInjectionComponent = DaggerMockInjectionComponent
            .builder()
            .mockNetworkModule(new MockNetworkModule())
            .build();
    mockInjectionComponent.inject(this);
}

Then I just Annotate my Injected Object.

EDIT : Do not forget to add testApt "com.google.dagger:dagger-compiler:$daggerVersion" at your app.gradle file .

Ivelius
  • 4,953
  • 7
  • 30
  • 54
Danny Beaumont
  • 356
  • 1
  • 8
  • Tried it already . It doesn't compile when you try to call DaggerMockInjectionComponent.builder() ... It says "... Error:(41, 54) error: cannot find symbol method builder() ..." Here is Github reference : https://github.com/ivelius/BitcoinGraph/blob/4b9080229f9a2f9c5362b4930bab3dcb979e9e08/app/src/test/java/com/example/yanbraslavski/bitcoingraph/MainPresenterTest.java#L43 – Ivelius Aug 29 '16 at 12:51
  • @Ivelius It has to be prepend with Dagger so DaggerMockAppComponent then make your project that Dagger can generate the file and it'll prompt you to import it. – Danny Beaumont Aug 29 '16 at 12:57
  • Yeah sure , was just a typo. I commited the fix. it still the same... Doesn't compile. You are free to checkout this small test project and see for yourself... https://github.com/ivelius/BitcoinGraph/blob/DaggerTests/app/src/test/java/com/example/yanbraslavski/bitcoingraph/MainPresenterTest.java – Ivelius Aug 29 '16 at 13:08
  • ok found it the last missing piece in your build.gradle add testApt 'com.google.dagger:dagger-compiler:2.5' then compile – Danny Beaumont Aug 29 '16 at 13:29
  • Just take my money Sir :) – Ivelius Aug 29 '16 at 13:34
  • 1
    Come on; you can't just write 'Then I just Annotate my Injected Object.'. You must realise this is an infuriating detail missed for people trying to learn Dagger/DI? Please just give us the code. – Chris Hatton Apr 18 '17 at 02:17
  • https://github.com/dracul89/dagger2example here's example it should help you learn dagger. – Danny Beaumont Apr 29 '17 at 18:50
  • Is this workaround working in Kotlin? I am using the kapt plugin and adding the equivalent dagger dependencies as mentioned in the comments with kaptTest, but I cannot get it working. – narko Apr 10 '18 at 11:29
3

As mentioned by the accepted answer. Do not forget to add :

For Java

Android Test

androidTestAnnotationProcessor 'com.google.dagger:dagger-compiler:$dagger_version'

JUnit test

testAnnotationProcessor 'com.google.dagger:dagger-compiler:$dagger_version'

For Kotlin

Android Test

kaptAndroidTest 'com.google.dagger:dagger-compiler:$dagger_version'

JUnit test

kaptTest 'com.google.dagger:dagger-compiler:$dagger_version'
Beulah Ana
  • 360
  • 2
  • 14
2

You don't need any dagger to test your presenter. Dagger's job is it to fullfill the dependencies of your classes (dependency injection).

For example you have this Presenter:

public class MyPresenter {

    Database database;
    ApiService apiService;

    @Inject
    public MyPresenter(final Database database, final ApiService apiService) {
        this.database = database;
        this.apiService = apiService;
    }
}

Dagger will provide your Presenter with the database and apiService objects for your presenter to use them. When running the actual app (not a test) these will be real objects with real functionality.

When testing the presenter, you want to test only the presenter, everything else should be mocked.

So when you create the presenter in your PresenterTest, you create it with mocked versions of database and apiService.

You can then test how your presenter interacts with these object by

a. mocking the objects behaviour like

when(database.getSomething()).thenReturn(something)

b. verify your presenter does what you want it to do with these objects like

verify(database).saveSomething()

(pseudo code)

Standard way to mock would be Mockito.

fweigl
  • 21,278
  • 20
  • 114
  • 205
  • Thanks for your answer. This is exactly what I do currently . But If I cannot use dagger to swap my modules (real module and test module) , than I loose one of the main reasons to use DI in my project. I would prefer to define a test module which will have those mocked database and apiService objects "provided" by dagger . That is the whole point of DI ! – Ivelius Aug 20 '16 at 08:55
  • 2
    If this is what you do currently, keep doing it. Else explain why you think you need a test component for your unit tests and post some code :) – fweigl Aug 21 '16 at 19:49
  • Maybe it is less about code , but more about the core understanding of DI. It is not a secret that DI makes it easier to write testable code. Check the Definition of DI in wikipedia. If we are injecting parameters ourselves to presenter during the test , we are bacically acting as an "injector" in the DI design pattern. Why not to use the framework for that ? And why do we need different modules in Dagger if they will never be interchanged ? – Ivelius Aug 22 '16 at 08:19
  • 1
    "Why not use the framework for that?" Because it's more work for no gains. Again, some code would be helpful to see why you think you need dagger components for your unit tests. "And why do we need different modules in Dagger if they will never be interchanged ?" Swapping real components for test components is really useful when doing Espresso (or other instrumentation tests), where you have no control over the instance creation of your classes (because Dagger creates instances at runtime) but still want them to use your mocked depenencies. – fweigl Aug 22 '16 at 11:50
0

You can swap out real modules with fake modules in two ways: do it at compile time using flavors as recommended by google architecture samples or at runtime by creating an abstract method which injects the real thing in production code and fake dependencies in test code. In the second case your test has to subclass the class that you want to mock and build the component from scrach

Tudor
  • 1,510
  • 1
  • 18
  • 17
  • When I use Flavors to substitute my Modules , I can do it even without dagger. As suggested in google example , they just have different versions of Injector class which acts like a provider. Basically they do DI without Dagger . The question is how to do it with dagger ? In general . If I need workarounds to do DI with Dagger , why do I need Dagger at all ? :D – Ivelius Aug 29 '16 at 13:05