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 4 of 4 of this article. Click here to view the first page.

Verify state

The following test is just a JUnit test:

  @Test
  fun addFavorite_shouldUpdateRecipeStatus() {
    // 1
    val recipe = Recipe("id", "title", "imageUrl", "sourceUrl", false)

    // 2
    presenter.addFavorite(recipe)

    // 3
    Assert.assertTrue(recipe.isFavorited)
  }
  1. Create a recipe.
  2. Call the addFavorite method.
  3. Verify that the recipe isFavorited flag has changed.

Run the test to make sure the state of a recipe is changed correctly when marked as a favorite.

RecipeRepository tests

Before creating the tests, open RecipeRepositoryImpl and look at the following functions:

  override fun addFavorite(item: Recipe) {
    // 1
    val favorites = getFavoriteRecipes() + item
    saveFavorites(favorites)
  }

  // 2
  private fun saveFavorites(favorites: List<Recipe>) {
    val editor = sharedPreferences.edit()
    editor.putString(FAVORITES_KEY, gson.toJson(favorites))
    editor.apply()
  }
  1. The addFavorite method, first calls getFavoriteRecipes and appends an item.
  2. Then it saves to SharedPreferences using a JSON format.

You’ll create a test that verifies this behavior.

Because you’ll need to stub getFavoriteRecipes() and also you’ll need to call the real addFavorite method, a mock on the RecipeRepository interface won’t work. You need to spy on a real instance of RecipeRepositoryImpl. More on the spy feature later.

Mock maker inline

Now, recall that Kotlin by default has final classes and final methods (unless you use the open keyword).
So, instead of adding the open keyword to the RecipeRepositoryImpl class and methods, create a text file under app/src/test/resources/mockito-extensions called org.mockito.plugins.MockMaker with the following content (it may be easier to switch to the Project view in the Project pane in order to add the new directory and text file):

mock-maker-inline

mockito-extensions file

This will enable you to mock/spy on real classes without adding the open keyword.

Spy the repository implementation

Add a new class to the test package called RepositoryTests with the following content:

package com.raywenderlich.ingredisearch

import android.content.SharedPreferences
import com.nhaarman.mockito_kotlin.mock
import com.nhaarman.mockito_kotlin.spy
import com.nhaarman.mockito_kotlin.whenever
import org.junit.Before

class RepositoryTests {
  private lateinit var spyRepository: RecipeRepository
  private lateinit var sharedPreferences: SharedPreferences
  private lateinit var sharedPreferencesEditor: SharedPreferences.Editor

  @Before
  fun setup() {
    // 1
    sharedPreferences = mock()
    sharedPreferencesEditor = mock()
    whenever(sharedPreferences.edit()).thenReturn(sharedPreferencesEditor)

    // 2
    spyRepository = spy(RecipeRepositoryImpl(sharedPreferences))
  }
}
  1. You need to mock SharedPreferences and the corresponding editor.
  2. Because you’ll stub the RecipeRepository‘s getRecipes method but you also need to call the real addFavorite method on the same object, instead of a mock you need a spy.

Finally, create the test:

  @Test
  fun addFavorite_withEmptyRecipes_savesJsonRecipe() {
    // 1
    doReturn(emptyList<Recipe>()).whenever(spyRepository).getFavoriteRecipes()

    // 2
    val recipe = Recipe("id", "title", "imageUrl", "sourceUrl", false)
    spyRepository.addFavorite(recipe)

    // 3
    inOrder(sharedPreferencesEditor) {
      // 4
      val jsonString = Gson().toJson(listOf(recipe))
      verify(sharedPreferencesEditor).putString(any(), eq(jsonString))
      verify(sharedPreferencesEditor).apply()
    }
  }
  1. Stub the getFavoriteRecipes() with an empty list. Notice that when stubbing spies you need to use doReturn/whenever/method.
  2. Call the real addFavorite method with a recipe.
  3. Check that the subsequent verifications are executed in the exact order.
  4. Verify that the list is saved correctly with JSON format.

Go ahead and run the repository tests to see the spy in action!

Where To Go From Here?

Congratulations! You’ve just learned the basics of using Mockito for unit testing.

You can download the final project here. Remember to add the keystore.properties file in order to open the project.

In this tutorial, you added the unit tests after writing the code. In Test Driven Development (TDD), you write the unit tests first and only add code to pass the currently failing unit test.

I suggest reading the following:

  • Mockito reference: To delve more into this topic, please have a look at the Mockito-Kotlin and Mockito wikis.
  • Mocks aren’t stubs: You’ll commonly hear in the jargon “You should mock that”, but they aren’t always strictly referring to mocks. An article from Martin Fowler explains the difference.
  • Dependency injection: In order to make your app more testable, it’s good to have your dependencies injected somehow. This Dagger 2 tutorial will help you with that.
  • Test patterns: Because writing tests is a bit of an art form, this book from Gerard Meszaros will explain some great patterns to you. It’s an incredible reference.
  • Espresso codelab: If you’re wondering how UI tests are done, this codelab from Google Developers will help you get started.

For even more testing practice, check out our book, Android Test-Driven Development by Tutorials.

I hope you enjoyed this tutorial, and if you have any questions or comments, please join the forum discussion below!