Testing in Android with Mockito. Image by Negative Space, Pexels.com

Testing in Android with Mockito

  • matise-seb
  • March 31, 2015

As I mentioned in my previous article about testing [Android testing with Robolectric], I have this little obsession with unit testing. Using Robolectric allows me to run the test in the local JVM instead of having to run an emulator or run the test on a real device. Now, Mockito allows me to mock, stub and spy classes to verify different behaviours in the code. We’re currently using Mockito to test the Android library we’re developing in the mobile team in Backbase.

According to Mockito creators:

Mockito is a mocking framework that tastes really good. It lets you write beautiful tests with clean and simple API. Mockito doesn’t give you a hangover because the tests are very readable, and they produce clean verification errors.

“Tastes really good, beautiful and doesn’t give a hangover.” Isn’t that just perfect?

Mockito configuration (Gradle)

If you’re using Gradle, integrating Mockito into your project couldn’t be easier. Just add:

dependencies {
    ...
    testCompile 'org.mockito:mockito-core:1.10.19'
    ...
}

If you’re not using Gradle, you can always download the jar file from the Mockito repository and add it to your Android project. (As of writing, 1.10.19 is the latest stable version.) According to the project page, they are working on the 2.0 version.

I’m going to use a particular example that I developed for the Android library. I wanted to have a simple cache system with the following interface:

public interface CacheManager {
    public void put(String id, String json) throws IOException;
    public String get(String id) throws IOException;
}

One of the implementations of this interface is going to be a file cache system. Here you can find the code of the implementation:

public class FileCacheManager implements CacheManager {

    private final String logTag = FileCacheManager.class.getSimpleName();
    private Context context;

    public FileCacheManager(Context context) {
        this.context = context;
    }

    @Override
    public void put(String id, String json) throws IOException {
        Closer closer = Closer.create();
        try {
            FileOutputStream outputStream = closer.register(context.openFileOutput(id, Context.MODE_PRIVATE));
            outputStream.write(json.getBytes());
        } catch (IOException e) {
            Log.e(logTag, "Error writing in the cache "+e.getMessage());
            throw new IOException("Error writing in the cache "+e.getMessage());
        }finally {
            try {
                closer.close();
            } catch (IOException e) {
                Log.w(logTag, "Warning! Something went wrong closing outputStream. Ignore this message");
                // ignore ...
            }
        }
    }

    @Override
    public String get(String id) throws IOException {
        FileInputStream inputStream = context.openFileInput(id);
        String storedString = new StringUtils().getStringFromInputStream(inputStream);
        return storedString;

    }

For the implementation of this class, I’m using the com.google.common.io.Closer class. You can find more information about this here.

Mockito annotations and setup

The latest versions of Mockito allowed us to create annotations to use mocks. In this example, I will use a real instance of FileCacheManager and I will inject mocks on it.

To construct a FileCacheManager object, I need the context. This is going to be a mock (actually, I don’t care about what the context is doing, so I will fake its behaviour).

@RunWith(RobolectricCustomTestRunner.class)
public class FileCacheManagerTest {

    @Mock Context fakeContext;
    CacheManager cacheManager;

    @Before
    public void setup() {
        MockitoAnnotations.initMocks(this);
        cacheManager = new FileCacheManager(fakeContext);
    }
...
}

As you can see, I’m using Robolectric test runner, because I have some calls to android classes inside my code. The @Mock annotation in front of an object declaration will make that object a mock – we will not have to instantiate it and we can fake its behaviour. It’s very important when using annotations to initialise the mocks using:

MockitoAnnotations.initMocks(this);

Mock and verify

So, until now, what do we have? An instance of FileCacheManager with a mock of Context. Now, let’s verify some behaviour. One of my tests will be:

I want to verify that when I use the method “put” the method “write” from the outputstream is called.

    @Test
    public void verifyOutputStreamCalls() throws Exception {
        String json = "{'json':'anyJson'}";
        when(fakeContext.openFileOutput("jsonModel", Context.MODE_PRIVATE)).thenReturn(fakeOutputStream);

        cacheManager.put("jsonModel", json);

        Mockito.verify(fakeOutputStream).write(json.getBytes());
        Mockito.verify(fakeOutputStream).close();
    }

I have divided the tests in three parts:

  • The first part is the preparation of the test, where I create variables that I will use on my tests, and I mock some behaviours. In this case, with the when…thenReturn structure, I’m faking already a behaviour, telling Mockito to return me a fakeOutputStream when anytime in the test the method “openFileOutput” is called from the mocked context.
  • The second part of the code is the actual call to the real method. cacheManager.put(“jsonModel”, json).
  • The last part is the verification. Mockito ‘remembers’ if a method has been called during the test execution. In this case, we verify that the fakeOutputStream calls the method write and the method close.

Using the when…thenReturn and the verify structures require the use of mocks (or spies). You can’t verify behaviours of real objects.

Throwing exceptions

Creating test if we are only going to test happy flows isn’t really useful, is it? Let’s test error scenarios. So, what happens if writing on the cache, there’s an error on the file. How will the code react? Here is a test to check that scenario:

  @Test(expected = IOException.class)
    public void verifyOutputStreamClosesWithIOException() throws Exception {
        String json = "{'json':'anyJson'}";
        when(fakeContext.openFileOutput("jsonModel", Context.MODE_PRIVATE)).thenReturn(fakeOutputStream);
        doThrow(new IOException()).when(fakeOutputStream).write(json.getBytes());

        cacheManager.put("jsonModel", json);

        Mockito.verify(fakeOutputStream).close();
    }

Again, we have the three parts, but in the first section we have added a new condition. Using “doThrow…when” allows us to throw exceptions anytime we want from any mocked object. So, in this case, when the mocked object fakeOutputStream calls the method, write an IOException will be thrown.

In this test, we verify that the outputStream is closed and that we are expecting an exception: @Test(expected = IOException.class) – this special annotation is specific of JUnit4.

Mockito is already interesting enough to write more about it. In future articles, I will touch on other Mockito features we’re using in our daily tests.

matise-seb