Testing Android Applications with Mockito and Dagger 2

I have been experimenting with Dependency Injection on Android for a few months now. But I never found a satisfying way to inject different dependencies when running them under test, without using a framework that uses reflection. A few days ago I read a very interesting article by Chiu-Ki Chan which revealed a very interesting way to work around this limitation. This fully fixed the problem I was running into and allows me to test my apps in a very clean and simple way.

I will discuss dependency injection with Dagger 2, but I won’t be exploring all of it’s options, such as scoped dependencies.

Dependency Injection

Dependency injection (DI) is a way to delegate the initialization of your dependencies out of your class. This means they are injected into your class in a different way. The Framework basically provides you with all of your dependencies.

Now let’s think how we call constructors. Usually we call them ourselves and initialize our state and dependencies. In case of DI, we don’t do this. We do create a constructor that receives all of the dependencies as parameters. This constructor is called by the DI framework. The Framework will construct a graph of dependencies and will initialize them as needed (depending on the Framework).

Dagger

Dagger is a DI framework developed by Square Inc.. In this article I’ll be using Dagger 2, which is developed by Google in cooperation with Square Inc.. Dagger 2 fixes the shortcomings of Dagger, and does contrarily to other DI Frameworks not use reflection. Everything is done at compile time using the Dagger compiler. This makes sure any issues are reported at compile time and greatly reduces the overhead of the Framework.

Understanding Dagger 2

Knowing all this, let’s first get the basics of Dagger 2 clear. In Dagger we have two basic concepts, Modules and Components. I will try to explain what each of them is. A Component is used to group modules together to satisfy all dependencies. Modules are used to provide the dependencies. Dagger 2 generates a factory for each of the dependencies that a module can provide. These factories use your provide method from the module.

As a result, you could swap Dagger Components to provide dependencies from different Modules and change the implementations. The concepts described in this paragraph are very important to understand, so please take your time to understand them as it is not easy to wrap your head around this immediately.

Using Dagger 2 in Android

We just talked about Dagger modules, they are not to be confused with Gradle modules. Gradle modules are part of your project. There is an app module, which contains the source-code of your app. You may also have a wear module and library modules in your project. Anyway, in this section module refers to Gradle modules.

To get started with Dagger in Android we need to configure Gradle. We need the android-apt Gradle plugin to add a new dependency scope to the app module in your project. This is used to be able to include the dagger compiler, without exposing it’s APIs in the project (it is a compile time dependency). So add android-apt to the top level build.gradle.

classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'

Next you need to add the Dagger and Dagger-compiler dependencies to the project. In the dependencies section of your app module add the following dependencies:

compile "com.google.dagger:dagger:${DAGGER_VERSION}"
apt "com.google.dagger:dagger-compiler:${DAGGER_VERSION}"
compile 'javax.annotation:jsr250-api:1.0'

Now the project is set up to include the dagger compiler. All we need to do now is apply the apt gradle plugin. At the top of the build.gradle file add:

apply plugin: 'com.neenbedankt.android-apt'

Now every time we build the dagger compiler will generate the required classes for us.

Dagger Components and Modules

Let’s say you created a class ApplicationComponent, then Dagger will generate the class DaggerApplicationComponent for you.

An ApplicationComponent could looks like this:

@Singleton
@Component(modules = {ApplicationModule.class})
public interface ApplicationComponent {

  void inject(MainActivity activity);

}

It specifies an inject method for each of the classes into which we need to manually inject our dependencies. These are typically fragments, activities and other classes we do not instantiate ourselves. (Remember that although we instantiate the fragments ourselves, they are also created by the framework, for example when restoring state). Our ApplicationComponent uses the ApplicationModule to provide the dependencies. This class looks like this:

@Module
public class ApplicationModule {

  private final Application mApplication;
  
  public ApplicationModule(Application app) {
    this.mApplication = app;
  }

  @Singleton
  @Provides
  Context provideApplicationContext() {
    return mApplication;
  }

  @Provides
  @Singleton
  SharedPreferences provideSharedPrefs() {
    return PreferenceManager.getDefaultSharedPreferences(mApplication);
  }
}

As you can see, this module can provide a Context and it can provide a SharedPreferences instance. Now these can be automatically injected into constructors of other classes. Note that Dagger can always inject classes that have a public default constructor. You don’t need to create an @Provides annotation for that.

Bootstrapping Dagger 2

To allow the entire application to use dagger, we need to initialize it in a common place. To do this we create our own Application subclass, and add it to the Manifest.

<application
  android:name=".MyApplication"
  android:icon="@mipmap/ic_launcher"
  ...

Now in the onCreate method of our own Application class, we create a method that initializes Dagger. We call this method initializeDagger. The method looks like this:

@Override protected void initializeDagger() {
  mApplicationComponent = DaggerApplicationComponent
    .builder()
    .applicationModule(new ApplicationModule(this))
    .build(); 
}

Because our module’s constructor needs an Application object, we need to set it on the builder. This application will be forwarded to the constructor of our module.

We now have everything set up and we can start using DI in our classes.

Using basic injection

Now that we have this all ready to use, let’s see how we can use it. In my sample project I also have a class called EventUtils which is provided in my module. This one I left out to simplify the example.

Now let’s say we have an SpinnerAdapter that we want to automatically inject into our activities and fragments. And this adapter has a dependency on the EventUtils class. In this case, we annotate the constructor to allow Dagger to create it for us. The class looks like this:

public class AccessLevelAdapter extends BaseAdapter {

  ...
  EventUtils mEventUtils;

  @Inject
  public AccessLevelAdapter(EventUtils eventUtils) {
    mEventUtils = eventUtils;
  }

}

Now in our fragments and activities we can just add a field like this:

@Inject AccessLevelAdapter mAdapter;

And the adapter will automatically be created by Dagger and it will automatically be injected into our class. This way you no longer need to concern yourself with setting it up. It will be done for you.

Using dependencies in Framework controlled classes

Because activities and fragments are constructed by the Android framework, we need something to allow Dagger to inject our dependencies into these classes. We create an Injector class for this.

public class Injector {

  public static void inject(MainActivity activity) {
    ((MyApplication) activity.getApplication()).
          getApplicationComponent().inject(activity);
  }
}

Now in the onCreate method of the MainActivity, we need to call this. We do this right after our call to super.onCreate.

@Override
protected void onCreate(Bundle savedInstanceState) {

  super.onCreate(savedInstanceState);
  Injector.inject(this);

  setContentView(R.layout.activity_main);
  ... 

}

In our MainActivity we have the following field defined:

@Inject SharedPreferences mPreferences;

This dependency is now automatically injected, right after we call the injector. As soon as that method returns all dependencies have been injected.

Mockito

Before we dive into creating the test-cases, let’s talk about Mockito. Mockito is a mocking framework. It simplifies creating mocks and removes the need to create mock implementations most of the time. This leads to cleaner and better readable tests.

For example, it allows us to set the result of a certain method call. This sounds a bit cryptic, so let me show you an example:

Mockito.when(mSharedPreferences.getInt("key", 0)).thenReturn(3);

Now this looks straight forward, doesn’t it? It says whenever getInt, with literal parameter “key” and literal value 0 is called, it should return 3. That’s all there is to this.

For me one of the biggest advantages on Android is that it can mock any class, for example service classes you can’t normally instantiate or mock yourself. So everything I am about to show you can also be applied to system services like NotificationManager, AlarmManager etc.

Mockito can do a lot more, like verifying a method was called on a mock with certain parameters, so be sure to read about it if you don’t know Mockito.

Now let’s test

DI is supposed to be the holy grail of loose coupling. So you can just swap implementations when you want to run your tests. But we just moved everything to compile time and removed all that is dynamic about it.

First, we will need to override the implementation of the Application class in our instrumentation classes. While this may seem impossible there is a way to do so.

Subclassing our Application class

The technique we’ll be using is based on what is described in this article: http://blog.sqisland.com/2015/12/mock-application-in-espresso.html

We will take advantage of how Instrumentation testing works. All tests a run by a TestRunner. Normally this is AndroidJUnitRunner. This class indirectly extends Instrumentation. The interesting thing is the newApplication method in this class. It’s Javadoc says: “Perform instantiation of the process’s Application object. The default implementation provides the normal system behavior”.

What we’ll do, is override this method in our own runner, so we can change the Application class used for the App. In this Application class we will override the Dagger Component with a different one for testing.

Our new Runner implementation looks like this:

public class MockJUnitRunner extends AndroidJUnitRunner {

  @Override
  public Application newApplication(ClassLoader cl, String className, Context context)
          throws InstantiationException, IllegalAccessException,
          ClassNotFoundException {
    return newApplication(MockApplication.class, context);
  }
}

It is important to set the runner in the build.gradle so it is actually used when the instrumentation test runs:

android {
  defaultConfig {
    ... 
    testInstrumentationRunner "com.appsimobile.weekly.MockJUnitRunner"
    ... 

Now when the tests run, it uses our MockApplication which is a subclass of our normal Application implementation.

Injecting our test module

Because we want to be able to use dagger in our test project, we need to enable the dagger compiler for that module as well. All you need to do is enable it in the dependencies of the build.gradle file.

androidTestApt "com.google.dagger:dagger-compiler:${DAGGER_VERSION}"

Next we need to subclass the existing Application class and override the initializeDagger method. This class looks like this:

public class MockApplication extends MyApplication {
  @Override
  protected void initializeDagger() {
    mApplicationComponent = DaggerMockApplicationComponent
          .builder()
          .mockApplicationModule(new MockApplicationModule(this))
          .build();
  }
}

Looking at this class, we notice that we still need to create the MockApplicationComponent and the MockApplicationModule. The module is where all of the magic happens. Instead of creating instances of the dependencies, we return mocked Mockito instances of the classes.

@Module
public class MockApplicationModule {

  private final Application mApplication;

  public MockApplicationModule(Application app) {
    this.mApplication = app;
  }

  @Singleton
  @Provides
  Context provideApplicationContext() {
    return mApplication;
  }

  @Provides
  @Singleton
  SharedPreferences provideSharedPrefs() {
    return Mockito.mock(SharedPreferences.class);
  }
}

As stated above, the most interesting part is that we return a mocked instance of the class.

The MockApplicationComponent should extend the ApplicationComponent to make it assignable to mApplicationComponent. This class looks like this:

@Singleton
@Component(modules = {MockApplicationModule.class})
public interface MockApplicationComponent extends ApplicationComponent {
  void inject(MainActivityTest app);
}

As you can see we can inject our dependencies in our tests. MainActivityTest in this case. This allows us to do some very cool things. But first let’s move to the test class.

A very important part of this class is the way we initialize everything. Using JUnit’s @Before annotation we can perform the injection. We do this by getting the instrumentation as below:

@Inject
SharedPreferences mSharedPreferences; 

@Before
public void setUp() {

  Instrumentation instrumentation = 
    InstrumentationRegistry.getInstrumentation();
  MockApplication app = (MockApplication) instrumentation
    .getTargetContext().getApplicationContext();

  MockApplicationComponent component =
    (MockApplicationComponent) app.getApplicationComponent();

  component.inject(this);
  reset(mSharedPreferences);

}

As a last step in this method we reset mSharedPreferences mock.

Now we can start writing tests with the mocks. We use espresso to write all of the tests. Setting up everything suddenly becomes very easy.

For example, MainActivity checks if it needs to show the on-boarding fragment. It does so by checking for a value in shared prefs. We can set the value to return with Mockito to test this. Our test now looks like this.

@Test
public void testSkipsOnBoarding() {

  when(mSharedPreferences.getBoolean(eq("shown_onboarding"), 
      anyBoolean())).thenReturn(true);

  mActivityRule.launchActivity(null);
  onView(withId(R.id.fab)).check(matches(isDisplayed()));
}

The first thing we do is set a mock on the preferences. Next, we launch the activity, and finally we check that the fab is visible. The FAB is not present in the on-boarding fragment so in that case the test will fail.

We can also create one for the other case. The case where we actually want to show the welcome flow:

@Test
public void testShowsOnBoarding() {

  when(mSharedPreferences.getBoolean(eq("shown_onboarding"),
      anyBoolean())).thenReturn(false);

  mActivityRule.launchActivity(null);
  onView(withText("Welcome")).check(matches(isDisplayed()));
}

Together we have created a maintainable and well working structure to test our app that makes use of Dagger 2. The technique used here can of course be used in other cases where you need to override some behavior of your Application class. But remember, there are not very much good reasons to have your own Application class in the first place. Most of the time this just generates overhead which should be prevented.

I applied this technique in my own testing project, Weekly . And Chiu-Ki Chan also posted a minimal example for this on Github.

That’s it for today’s post. If this post helped you, or you have any questions, please leave a comment.

Cheers,

Nick

This post first appeared on the warm beer blog.

2 Replies to “Testing Android Applications with Mockito and Dagger 2”

  1. Hi.
    First of all ?I have to say that this is a very good intro to Dagger 2. It really helped me since I have just started using DI in android.
    One thing I’d like to ask you : I can’t see the benefits in testing using Dagger. What I mean is, couldn’t we just use Mockito to mock SharedPreferences in MainActivityTest ? Something like : SharedPreferences mSharedPreferences = Mockito.mock(SharedPreferences.class) instead of the following :

    @Inject
    SharedPreferences mSharedPreferences;

    Why we need to do it through Dagger the mocking and not in the MainActivityTest class ? Since it’s just a mock and is the same for every class we test why we have to do all of these configurations with such an overhead ?
    Thanks!

    1. Hi Teo, Thank you!

      The reason it is the same for every class is because it is injected by Dagger. In the test cases Dagger injects a mock, the same mock, into each of the classes that need it. Dagger’s @Singleton annotation makes sure of this. If you wouldn’t use dagger you would need to insert the mock into each of the classes manually.

      Cheers,
      Nick

Leave a Reply

Your email address will not be published. Required fields are marked *