Step #7: Setting Up a Mock Repository
Now, we can start setting up some tests. As part of this, you will start to discover that testing code frequently has strange restrictions and requirements, above and beyond the strange restrictions and requirements that you see in standard Android app development.
Next, change SingleModelMotorTest
to be:
package com.commonsware.todo.ui
import com.commonsware.todo.MainDispatcherRule
import com.commonsware.todo.repo.ToDoModel
import com.commonsware.todo.repo.ToDoRepository
import com.nhaarman.mockitokotlin2.doReturn
import com.nhaarman.mockitokotlin2.mock
import kotlinx.coroutines.flow.flowOf
import org.junit.Test
import org.junit.Assert.*
import org.junit.Before
import org.junit.Rule
class SingleModelMotorTest {
@get:Rule
val mainDispatcherRule = MainDispatcherRule(paused = true)
private val testModel = ToDoModel("this is a test")
private val repo: ToDoRepository = mock {
on { find(testModel.id) } doReturn flowOf(testModel)
}
private lateinit var underTest: SingleModelMotor
@Before
fun setUp() {
underTest = SingleModelMotor(repo, testModel.id)
}
}
There is quite a bit to explain in these few lines of code.
@get:Rule
val mainDispatcherRule = MainDispatcherRule(paused = true)
This applies our MainDispatcherRule
as a JUnit rule to our JUnit test.
The @get:Rule
syntax is a side-effect of the way Kotlin integrates with Java. If this were a Java class, we would annotate our rule field with @Rule
. @get:Rule
says “add the @Rule
annotation to the getter function associated with this property”. JUnit’s annotation processor supports the @Rule
annotation being on a field or on a getter method, so @get:Rule
allows that annotation processor to work with a Kotlin property.
private lateinit var underTest: SingleModelMotor
@Before
fun setUp() {
underTest = SingleModelMotor(repo, testModel.id)
}
We then have an underTest
property for our SingleModelMotor
. underTest
is a common name in unit tests for “the instance of the class that we are testing”.
@Before
is a JUnit annotation that says “run this function before each of the test functions”. Here, we create our SingleModelMotor
instance. Ideally, we would just use a val
and initialize our SingleModelMotor
that way, skipping this setUp()
function. Unfortunately, our MainDispatcherRule
will not have had a chance to do its work yet. So, we are forced to use this approach, so the MainDispatcherRule
can fix up the threading before we try creating a SingleModelMotor
instance.
private val testModel = ToDoModel("this is a test")
private val repo: ToDoRepository = mock {
on { find(testModel.id) } doReturn flowOf(testModel)
}
In Android testing, we use mocks for two main things.
One is for creating a fake instance of some object, one that we teach how to respond to various function calls. This is not the object that we are trying to test, but it is some object that is needed by what we are trying to test… such as a motor needing a repository. We use the mock instead of a real instance of the object for a variety of reasons:
- To have faster tests (e.g., to avoid database I/O)
- To provide specific responses to calls (particularly for server calls that we cannot control in the tests)
- To test scenarios that are difficult to recreate using the real object (e.g., server failures)
Another is to track which calls are made on the object. That way not only can our tests supply input to the object being tested, but we can examine the output, in the form of calls to the mock, and confirm that those calls did what we want.
Here, we use a mock()
function from Mockito to set up a mock implementation of ToDoRepository
. It generates an instance of a generated subclass of ToDoRepository
, one where we can dictate how it behaves in our test code, rather than relying on the real ToDoRepository
implementation. Specifically, we have it return a manually-created ToDoModel
instance (testModel
) when something tries finding a model with that model’s id
.
Prev Table of Contents Next
This book is licensed under the Creative Commons Attribution-ShareAlike 4.0 International license.