We will work on the following piece of code. It's simplified version of Presenter in Model-View-Presenter pattern. Let's see what we can and should unit test there.

interface View {
    fun displayItems(list: List<Element>)
    fun displayDetails(element: Element)
    fun displayError()
}

data class Element(val id: Int, val content: String)

interface DataProvider {
    fun getAll(): List<Element>
    fun getOne(id: Int): Element?
}

class Presenter(
    val view: View,
    val provider: DataProvider
) {
    fun start() {
        with(provider.getAll()) {
            if (this.isNotEmpty()) {
                view.displayItems(this)
            } else {
                view.displayError()
            }
        }
    }

    fun getOne(id: Int) {
        provider.getOne(id)?.let {
            view.displayDetails(it)
        }
    }
}

We will be performing tests here on mocked View with stubbed DataProvider methods:

  • when DataProvider.getAll() returns non-empty list of elements, then we display that list on View
  • when DataProvider.getAll() returns empty list, we display error on View
  • when View asks presenter for element described by given id we display that element if we have it in our repository, otherwise we display error

We will check only the first test case - happy path of displaying two elements on view.

Other test cases are left as an exercise to the reader

Test using vanilla Mockito

import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.junit.MockitoJUnitRunner

@RunWith(MockitoJUnitRunner::class)
class PresenterTest {

    @Mock
    lateinit var view: View

    @Mock
    lateinit var dataProvider: DataProvider

    @Test
    fun `display non-empty list`() {
        val elements = listOf(
            Element(1, "first"),
            Element(2, "second")
        )

        Mockito.`when`(dataProvider.getAll()).thenReturn(elements)

        val presenter = Presenter(view, dataProvider)

        presenter.start()

        Mockito.verify(view).displayItems(elements)
    }
}

The test flow here consists of the following steps:

  • we declare class-level mocks with @Mock annotation and lateinit var modifier
  • then we configure DataProvider stub with Mockito.`when`(...).thenReturn(...) function
  • we execute system under test entry method (presenter.start())
  • we verify with Mockito that given elements were displayed on view with Mockito.verify(...) method

To make @Mock annotations discoverable by framework, we must add MockitoJunitRunner to the whole test class.

Other way of configuring mocks would be declaring them directly in test method, instead of top of the class:

import org.junit.Test
import org.mockito.Mockito

class PresenterTest {

    @Test
    fun `display non-empty list`() {
        val elements = listOf(
            Element(1, "first"),
            Element(2, "second")
        )

        val dataProvider: DataProvider = Mockito.mock(DataProvider::class.java)

        Mockito.`when`(dataProvider.getAll()).thenReturn(elements)

        val view: View = Mockito.mock(View::class.java)

        val presenter = Presenter(view, dataProvider)

        presenter.start()

        Mockito.verify(view).displayItems(elements)
    }
}

We got rid of @Mock and @RunWith annotations - and also we can now declare those parameters as val instead of lateinit var.

Yet, mock configuration could me more Kotlin idiomatic.

Let's try to make use of Mockito-Kotlin extensions

Refactoring with Mockito-Kotlin extensions

import com.nhaarman.mockitokotlin2.doReturn
import com.nhaarman.mockitokotlin2.mock
import org.junit.Test
import org.mockito.Mockito

class PresenterTest {

    @Test
    fun `display non-empty list mockito-kotlin`() {
        val elements = listOf(
            Element(1, "first"),
            Element(2, "second")
        )

        val dataProvider: DataProvider = mock {
            on { getAll() } doReturn elements
        }

		val view: View = mock()

        val presenter = Presenter(view, dataProvider)

        presenter.start()

        Mockito.verify(view).displayItems(elements)
    }
}

Let's look at method mock{} definition from com.nhaarman.mockitokotlin2.mock package:

As we can see, it's reified method - type returned by that method will be inferred in proper context, so without passing extra type information we can write:

val dataProvider: DataProvider = mock()

Last argument of that method is stubbing: KStubbing<T>.(T)->Unit. In Kotlin, last functional argument of method could be reduced to just opening lambda in declaration:

val dataProvider: DataProvider = mock {

}

Now let's see how we can make use of KStubbing method:

It will delegate our stubbing logic to regular Mockito calls.

val dataProvider: DataProvider = mock {
	on { getAll() }
}

Then we can introduce more Kotlin-specific invocations by using infix fun doReturn:

doReturn is infix fun - we can use it without "()":

val dataProvider: DataProvider = mock {
	on { getAll() }.doReturn(listOf(Element(1, "first)))
}

// or using infix notation:
val dataProvider: DataProvider = mock {
	on { getAll() } doReturn listOf(Element(1, "first))
}

And finally our test will look like this:

import com.nhaarman.mockitokotlin2.doReturn
import com.nhaarman.mockitokotlin2.mock
import org.junit.Test
import org.mockito.Mockito

class PresenterTestVanillaMockito {

    @Test
    fun `display non-empty list`() {
        val elements = listOf(
            Element(1, "first"),
            Element(2, "second")
        )

        val view: View = mock()
        val dataProvider: DataProvider = mock {
            on { getAll() } doReturn elements
        }

        val presenter = Presenter(view, dataProvider)

        presenter.start()

        Mockito.verify(view).displayItems(elements)
    }
}

Summary

Mockito-Kotlin extensions are fairly easy and fun to use - they introduce DSL for mocks and stubs, increasing readability and helping avoid long complex mock configurations in test suite.    

Resources

nhaarman/mockito-kotlin
Using Mockito with Kotlin. Contribute to nhaarman/mockito-kotlin development by creating an account on GitHub.
mockito/mockito
Most popular Mocking framework for unit tests written in Java - mockito/mockito

Header photo by Mae Mu on Unsplash

Get new posts directly to your inbox, subscribe to our newsletter

* indicates required

For GDPR compliance, I have to ask you to allow me to send emails to you. I will use your email address to notify you about new content on my site.

You can unsubscribe at any time by clicking the link in the footer of our emails. For information about our privacy practices, please visit our website.

We use Mailchimp as our marketing platform. By clicking below to subscribe, you acknowledge that your information will be transferred to Mailchimp for processing. Learn more about Mailchimp's privacy practices here.