Testing your app with dagger 2 and AndroidInjectionModule

Alejandro Vidal Rodriguez
AndroidPub
Published in
4 min readApr 4, 2017

--

We want to build our apps as decoupled as possible and injecting those dependencies help, but if that dependency injection is hurting the testing then we need to find a fix for it!

The latest release of dagger 2 (2.10) brings some new classes to fix some issues on android development.

The AndroidInjectionModule helps handling the android framework classes like Activity, Fragment, Service and Broadcasts.

We will still follow the same concepts from dagger, we will still have modules to @provide stuff.

This would be our Rest modules, Db modules, Navigation modules… I like to think of them as concepts, something that provides what is need to handle the cart, something that gives you what you need to share app content, the network layer communication, etc.

But there was one problem with the activity injection and testing, if the activity had @injects for the flumbolator TM it would also need to know the component that it was injecting too!

So if we wanted to test this activity with a mock or an specific implementation of the dependency we would have to test against an activity that extends our activity and override the injection.

Thanks to the new structure this is all the activity knows about its injector.

@Override
protected void onCreate(Bundle savedInstanceState) {
AndroidInjection.inject(this);
super.onCreate(savedInstanceState);
}

To achieve this we need to start creating subcomponents for each Activity:

This subcomponent can have its own module for things that only attach to this activity, like the presenter.

The important part is to extend from AndroidInjector<YourActivity> and provide a builder annotated with @Subcomponent.Builder

In the modules definition you can define all the modules that this activity is going to require, for us this is the super fantastic frumbolator module (yes I know that I call it different every time :)

How does it know dagger where to get the injection for MainActivity if the activity is just calling AndroidInjection.inject(this)?

With a map that relates the class with the builder, we will call this module the BindingModule

with this approach we need to define the subcomponents and use the @binds and @IntoMap annotations, this is just like saying in an old java map put(Activity.class, Binder method) so for each class we know which builder needs to be called.

And where is this map being added? in the AppComponent

And last but not least we need our Application class.

we have the HasDispatchingActivityInjector interface and we need to return it and we inject ourselves in the DaggerMyApplicationcomponent (remember this is a generated class so you need to rebuild)

Ok but why all these changes, its true that I don’t need to write some boilerplate on the activity code but now I have to remember to add it to the map binding the activity submodule, etc (luckily the errors are quite informative and helps a lot in these cases)

Well the reason is that now we can do proper testing now!

We are going to use Mockito, Roboelectric and Junit.

The goal is to test a public method in the activity without testing the Injected flumbolator, so we want to have a mock that returns something that we control and do fast testing (some see the light on TDD again)

As you may have notice with roboelectric we can set the application class that we are going to use, yes that is our entry point!

There is one issue that test project does not find the generated classes by dagger so you need to add this to your gradle file

With that fix now your test files will be able to find generated classes.

Those ugly print lines are to see them in test runner and realize that you can change between the real application and the test with mocks or between mocks :)

Ok so on this testComponent we define our TestBindingModule to provide TestSubComponents that will use TestModules, yes I know, sounds a little bit too much but its worth it.

and the testModule that provides the mock flumbulator TM.

Now if we run our TestMainActivity it will work with the mock Flumbolator and life will be wonderful, go and make those test that can be run in your CI without an emulator and hey the next time you fix a bug create a test for it!

Clone, fork and make PR on the sample project here :)

Share your thoughts in the comments! And if you like this story and think it would be helpful for others click on like and follow me for future dev stories.

--

--