Simplify mocking Dagger2 dependencies with DaggerMock

Dagger2 is a great improvement over its predecessor, but tasks like mocking dependencies for system tests can still be quite verbose to setup, which is where the DaggerMock library comes in handy!

Before DaggerMock trying to provide mocked objects required (at the very least) a new module that will provide the mocked dependency and then depending on the approach you take having to build a dependency graph that includes your mocked module and all other non-mocked modules. The test below taken from a demonstration project I put together shows just some of the extra work just to inject a mocked ButtonTextService.

public class TestButtonInUi extends UiTest {

    @Singleton
    @Component(modules = {AppModule.class, MockedButtonModule.class})
    interface TestButtonInUiAppComponent extends AppComponent {
        void inject(TestButtonInUi activity);
    }

    @Rule
    public final ActivityTestRule<MainActivity> activityRule = new ActivityTestRule<>(MainActivity.class, false, false);

    @Inject
    ButtonTextService buttonTextService;

    @Before
    public void setupDagger() {
        final TestButtonInUiAppComponent component = buildAppComponent();
        getApp().setComponent(component);

        component.inject(this);
    }

    @Test
    public void buttonUpdatedWithText() {
        when(buttonTextService.getText()).thenReturn("Test456");

        activityRule.launchActivity(null);

        onView(withId(R.id.button)).check(matches(withText("Test456")));
    }

    private static TestButtonInUiAppComponent buildAppComponent() {
        return DaggerTestButtonInUi_TestButtonInUiAppComponent.builder()
                .mockedButtonModule(new MockedButtonModule())
                .build();
    }
}

Adding DaggerMock to your project is a doddle and it does away with all the aforementioned setup code. By adding in the DaggerMockRule and pointing it to your existing component/modules it will automatically look for @Mock annotated fields then override the provider methods with a mock, which is then set in the field for stubbing.

public class TestButtonInUi extends UiTest {

    @Rule
    public DaggerMockRule<AppComponent> daggerRule = new DaggerMockRule<>(AppComponent.class,
            new AppModule(getApp()), new ButtonModule()).set(new DaggerMockRule.ComponentSetter<AppComponent>() {
        @Override
        public void setComponent(AppComponent appComponent) {
            getApp().setComponent(appComponent);
        }
    });

    @Rule
    public final ActivityTestRule<MainActivity> activityRule = new ActivityTestRule<>(MainActivity.class, false, false);

    @Mock
    private ButtonTextService buttonTextService;

    @Test
    public void buttonUpdatedWithText() {
        when(buttonTextService.getText()).thenReturn("Test123");

        activityRule.launchActivity(null);

        onView(withId(R.id.button)).check(matches(withText("Test123")));
    }
}

For me the ease with which I could inject mocked objects into my project was enough, but along with custom rules and support of submodules (although limited) it also has the InjectFromComponent which provides access to objects injected into classes listed in the component.

If you have any suggestions on how I can improve this article, or my writing then please let me know @SketchingDev