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:

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.