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.