fbpx

Using Mockito in Kotlin projects

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

https://github.com/mockito/mockito
https://github.com/mockito/mockito-kotlin

Header photo by Mae Mu on Unsplash


more insights

Uncategorized
Jarosław Michalik

#kotlinDevChallenge 2023 summary

The KotlinDevChallenge was not just a competition, but a journey of learning, growth, and community engagement. Every participant in this challenge is a winner in

Read More »

AndroidPro newsletter 🚀

join 3057 developers reading AndroidPro newsletter every week

👉 tips & tricks, articles, videos

👉 every Thursday in your inbox

🎁 bonus on signup – Clean Architecture blueprint

brought to you by Jarek Michalik, Kotlin GDE

You can unsubscribe any time. For details visit our privacy policy.

android pro newsletter tips and tricks how to learn android