Android Unit Testing with Mockito

In this Unit Testing with Mockito tutorial for Android, you will learn how to refactor an app in a way that makes it easy to write unit tests in Kotlin using Mockito. By Fernando Sproviero.

Leave a rating/review
Save for later
Share
You are currently viewing page 3 of 4 of this article. Click here to view the first page.

MVP Refactor done!

Now that the refactor was done, you’ll be able to create unit tests.

Build and run the app and make sure the app behaves just as it did before the refactor.

You can also download the MVP refactored project if you want to. Remember to add the keystore.properties file in order to open the project.

Using Mockito

Setup Mockito dependencies

Open the application’s build.gradle file and add the following dependency:

dependencies {
  ...
  testImplementation 'com.nhaarman:mockito-kotlin-kt1.1:1.5.0'
  ...
}

Mockito-Kotlin is a wrapper library around Mockito.

It provides top-level functions to allow for a more idiomatic approach and also solves a few issues with using the Mockito java library in Kotlin. Check its wiki for more information.

State & Behavior verification

In state verification, you first have the system under test (SUT) configured with all the necessary dependencies (setup phase). Secondly, you perform a certain operation (exercise phase). Finally, you examine the state of the object and verify it’s the expected one (verification phase). This is sometimes called black-box testing. Usually, JUnit is used for this.

In behavior verification, instead, you specify which methods are to be invoked on the dependencies, in other words, you setup expectations, thus verifying not that the ending state is correct, but that the methods were invoked correctly. This is sometimes called white-box testing. Usually, Mockito is used for this.

Mockito main features

  • Mock:
    The SUT usually has dependencies that are necessary for the SUT to work. If you want to verify a method was called by the SUT, you mock the dependency class and then verify the mocked dependency method was called.
  • Stubbing:
    Sometimes your SUT will follow a certain path or another depending on the results of methods called on its dependencies. To force a dependency to always respond the same way, you need to stub the method.

At last, it’s time for some unit testing! :]

Search unit tests

You’ll create a unit test that verifies that the presenter calls the view’s showQueryRequiredMessage when an empty search is performed.

First delete the ExampleUnitTest:
Delete ExampleUnitTest

Add a new class called SearchTests, with the following content:

package com.raywenderlich.ingredisearch

import com.nhaarman.mockito_kotlin.mock
import com.nhaarman.mockito_kotlin.verify
import org.junit.Test

class SearchTests {
  // 1
  @Test
  fun search_withEmptyQuery_callsShowQueryRequiredMessage() {
    // 2
    val presenter = SearchPresenter()
    // 3
    val view: SearchPresenter.View = mock()
    presenter.attachView(view)

    // 4
    presenter.search("")

    // 5
    verify(view).showQueryRequiredMessage()
  }
}
  1. Every unit test must start with the @Test annotation.
  2. Instantiate the presenter.
  3. Because you don’t need a real view that conforms to the interface, you can mock it.
    Then attach this view to the presenter.
  4. Call the search method with an empty query.
  5. Verify on the mocked view that the presenter calls showQueryRequiredMessage

Now to run the test, press on the little green arrow (the arrow will have a circle after the first run of the tests) to the left of the class declaration:

Run SearchTests

Hopefully you’ll get all green! :]

Successful test results

Refactoring the unit test

Because all SearchTests will basically share the same setup, you should add a setup() function with a @Before annotation as follows:

class SearchTests {

  private lateinit var presenter : SearchPresenter
  private lateinit var view : SearchPresenter.View

  @Before
  fun setup() {
    presenter = SearchPresenter()
    view = mock()
    presenter.attachView(view)
  }
  ...

The function marked @Before is executed before any test, therefore it’s a good place to setup objects/mocks. It’s also common to see a tearDown() function marked @After in test classes, which is run when a test completes.

Modify your first test with the following:

  @Test
  fun search_withEmptyQuery_callsShowQueryRequiredMessage() {
    presenter.search("")

    verify(view).showQueryRequiredMessage()
  }

Verify a method is never called

You can also test that a function is never called by adding the following test:

  @Test
  fun search_withEmptyQuery_doesNotCallsShowSearchResults() {
    presenter.search("")

    verify(view, never()).showSearchResults(anyString())
  }
Note: Android Studio will mark never() as an unresolved reference. Make sure you import the com.nhaarman.mockito_kotlin option which is the Mockito wrapper.

Search results tests

Kotlin default final classes/methods

Before proceeding to create these tests, it’s important to mention that Kotlin classes and methods by default are final. Mockito won’t work with final classes/methods, but there are a few workarounds:

  • Add the open keyword to classes and methods that you’ll mock.
  • Create an interface and have the class implement the interface. Then, just mock the interface (interfaces are open by default).
  • Use mock-maker-inline. You’ll do this later.

Repository refactor

Because in the following tests you’ll mock the repository, we’re going to take the interface approach.

So, use Shift+F6 to rename the RecipeRepository class to RecipeRepositoryImpl and create a new RecipeRepository interface with all the public methods, you can also move the RepositoryCallback outside:

interface RecipeRepository {
  fun addFavorite(item: Recipe)
  fun removeFavorite(item: Recipe)
  fun getFavoriteRecipes(): List<Recipe>
  fun getRecipes(query: String, callback: RepositoryCallback<List<Recipe>>)
}

interface RepositoryCallback<in T> {
  fun onSuccess(t: T?)
  fun onError()
}

Open RecipeRepositoryImpl and implement RecipeRepository:

class RecipeRepositoryImpl(private val sharedPreferences: SharedPreferences) :
    RecipeRepository

Add the override keyword to the corresponding methods (also, be sure to remove RepositoryCallback from RecipeRepositoryImpl if you added it into the interface file).

Also, open SearchResultsPresenter and modify it to use the interface:

class SearchResultsPresenter(val repository: RecipeRepository) :
    BasePresenter<SearchResultsPresenter.View>()

And fix the anonymous RepositoryCallback:

repository.getRecipes(query, object : RepositoryCallback<List<Recipe>> {

Setup Search results tests

Add a new class called SearchResultsTests to the test package:

package com.raywenderlich.ingredisearch

import com.nhaarman.mockito_kotlin.mock
import org.junit.Before

class SearchResultsTests {

  private lateinit var repository: RecipeRepository
  private lateinit var presenter: SearchResultsPresenter
  private lateinit var view: SearchResultsPresenter.View

  @Before
  fun setup() {
    repository = mock()
    view = mock()
    presenter = SearchResultsPresenter(repository)
    presenter.attachView(view)
  }
}

Add the following tests:

  // 1
  @Test
  fun search_callsShowLoading() {
    presenter.search("eggs")

    verify(view).showLoading()
  }

  // 2
  @Test
  fun search_callsGetRecipes() {
    presenter.search("eggs")

    verify(repository).getRecipes(eq("eggs"), any())
  }

These tests verify:

  1. That the view is shown as “loading” whenever the presenter is required to search.
  2. If the repository is called with the corresponding parameters (“eggs”), then the eq matcher is used to verify that it’s called with the same string. The any matcher was used for the callback parameter because you don’t care about it.

Try and run these search results tests using the green arrow next to the test class name.