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.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Android Unit Testing with Mockito
30 mins
- Testing pyramid
- Getting started
- Is this unit testable?
- Logic in the Activities
- Activity doing everything
- Adapter doing too much logic
- Conclusion
- Model-View-Presenter
- Refactoring to MVP
- Creating your first Presenter
- Implementing your first View
- Creating the SearchResults presenter
- Implementing SearchResultsPresenter.View
- MVP Refactor done!
- Using Mockito
- Setup Mockito dependencies
- State & Behavior verification
- Mockito main features
- Search unit tests
- Refactoring the unit test
- Verify a method is never called
- Search results tests
- Kotlin default final classes/methods
- Repository refactor
- Setup Search results tests
- Stub a callback
- Verify state
- RecipeRepository tests
- Mock maker inline
- Spy the repository implementation
- Where To Go From Here?
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:
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()
}
}
- Every unit test must start with the @Test annotation.
- Instantiate the presenter.
- Because you don’t need a real view that conforms to the interface, you can mock it.
Then attach this view to the presenter. - Call the search method with an empty query.
- 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:
Hopefully you’ll get all green! :]
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())
}
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:
- That the view is shown as “loading” whenever the presenter is required to search.
- 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.