简体   繁体   中英

How to inject mocks for class (vs interface) for Dagger

First of all, I am trying to come up with some reasonable way to do unit testing on Android. Mostly I am concerned with POJO objects.

In my previous projects, I just injected all dependencies through constructor. I created mocks in my unittests and injected these mocks through constructor too. The downside is huge constructors and passing parameters through multiple layers of code.

Obvious solution is dependency injection framework. I looked a couple of them which work on Android and decided to use Dagger. I figured it out and updated my apps to use it.

Now, I want to update unittests and I am not sure how do I change objectGraph to use mocks (vs real classes).

I saw this article: https://gist.github.com/adelnizamutdinov/7483963 However, it only shows to inject mocks for @Provideds annotation. And it's not clear how to inject mocks for classes (when you don't have provided method in module)

Update 1 (to Eugen Martynov)

Based on Dagger documentation:

By default, Dagger satisfies each dependency by constructing an instance of the requested type as described above. When you request a CoffeeMaker, it'll obtain one by calling new CoffeeMaker() and setting its injectable fields.

But @Inject doesn't work everywhere:

Interfaces can't be constructed. Third-party classes can't be annotated. Configurable objects must be configured!

For these cases where @Inject is insufficient or awkward, use an @Provides-annotated method >to satisfy a dependency. The method's return type defines which dependency it satisfies.

So, it looks like it can satisfy dependencies without @Provides. However, only to inject classes (vs interfaces).

Update 2

My case is following:

public class Bar {

    @Inject
    Bar() {
    }

    public void doSomethingElse() {
    }
}

public class Foo {

    @Inject
    Bar bar;

    public void doSomething() {
        bar.doSomethingElse();
    }
}

public class FooTest
{

    @Test
    void test_doSomething()
    {
         // I want to create a mock of Bar here and inject it to foo
         // so I can replace all dependencies with mocks before calling 
         // class under test

         foo.doSomething();

    }

}

Dagger will take care of injecting classes which are annotated with @Inject without any further action from you. But if you want to take control of how your class is being created you simply need to add a @Provide method in one of your modules, a Test module preferably in your case and then do the creation yourself.

Your class which by default if injected by Dagger without any intervention from you would simply return the value "DEFAULT"

public class MyClass {

    @Inject
    MyClass() {

    }

    public String getValue() {
        return "DEFAULT";
    }
}

Somewhere in one of your Test modules

@Provide
MyClass provideMyClass() {
    return Mockito.mock(MyClass.class)
}

Then when running a test

@Inject
MyClass myClass;

// code here to actually do the injection

Mockito.when(myClass.getValue()).thenReturn("MOCK");

myClass.getValue(); // Should return "MOCK" instead of "DEFAULT"

EDIT: After discussion with @Victor this is what I suggested he could do to achieve is goal but he's already built a small field injection tools to achieve this directly. Here's the code in case it can be useful to anyone else.

public class FooTest {

    @Mock
    private Bar mockBar;

    @Before
    public void setup() {
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void useDaggerModuleWithMock() {
        ObjectGraph objectGraph = ObjectGraph.create(new FooMockedTestModule());

        when(mockBar.doSomethingElse()).thenReturn("MOCK");

        Foo foo = new Foo();
        objectGraph.inject(foo);

        assertThat(foo.doSomething(), is("MOCK"));
    }

    @Module(injects = Foo.class)
    public class FooMockedTestModule {
        // We now take advantage of the module and provide our own implementation of the Bar class instead of letting
        // Dagger do the instance creation itself.
        @Provides
        Bar provideMockBar() {
            return mockBar;
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM