The System Under Test
interface RandomNumberProvider {
fun random(): Int
}
We’ll be stubbing this interface – one method, returning random Integer.
doReturn with Mockito-Kotlin
Now let’s create test:
package com.kotlintesting.stubbing
import org.junit.jupiter.api.Test
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import kotlin.random.Random
/**
* Created by jaroslawmichalik
*/
class DoReturnVsDoAnswer {
@Test
fun `just repeat 10 times`() {
val timeProvider = mock<RandomNumberProvider> {
on { random() } doReturn Random.nextInt()
}
repeat(10) {
println("Return " + timeProvider.random())
}
}
}
We have no assertion in here – I’m just checking mock behavior manually and looking into the logs.
What’s happening here?
- Create mock with Mockito-Kotlin
- stub
random()
method fromRandomNumberProvider
withdoReturn
- return
Random.nextInt()
- Use Kotlin built-in method
repeat
- Inside
repeat
block invoke stubbed method 10 times.
Run this test and see results:
Ok – we can see, that while stubbing method with doReturn
, return value is resolved once and then each invocation gives us the same result.
Now let’s check doAnswer
.
doAnswer with Mockito-Kotlin
package com.kotlintesting.stubbing
import org.junit.jupiter.api.Test
import org.mockito.kotlin.doAnswer
import org.mockito.kotlin.mock
import kotlin.random.Random
/**
* Created by jaroslawmichalik
*/
class DoReturnVsDoAnswer {
@Test
fun `just repeat 10 times`() {
val timeProvider = mock<RandomNumberProvider> {
on { random() } doAnswer {
Random.nextInt()
}
}
repeat(10) {
println("Return " + timeProvider.random())
}
}
}
Exactly like in doReturn example – no assertion in here. We’ll be checking logs.
Step by step:
- Create mock with Mockito-Kotlin
- stub
random()
method fromRandomNumberProvider
withdoAnswer
- in
doAnswer
create function returningRandom.nextInt()
- Use Kotlin built-in method
repeat
- Inside
repeat
block invoke stubbed method 10 times.
And here’s the results:
Each time different number.
Stubbing method with doAnswer makes our code evaluate expression inside doAnswer
block every time it’s invoked.
Using returns and answers in MockK
The same stubbing mechanism is present in MockK:
package com.kotlintesting.stubbing
import io.mockk.every
import io.mockk.mockk
import org.junit.jupiter.api.Test
import kotlin.random.Random
class DoReturnVsDoAnswerMockk {
@Test
fun `it should return`() {
val timeProvider = mockk<RandomNumberProvider> {
every { random() } returns Random.nextInt()
}
repeat(10) {
println("Return " + timeProvider.random())
}
}
@Test
fun `it should answer`() {
val timeProvider = mockk<RandomNumberProvider> {
every { random() } answers {
Random.nextInt()
}
}
repeat(10) {
println("Answer " + timeProvider.random())
}
}
}
By using returns
and answers{}
we will achieve the same behavior.
val timeProvider = mockk<RandomNumberProvider> {
every { random() } returns Random.nextInt()
}
val timeProvider = mockk<RandomNumberProvider> {
every { random() } answers {
Random.nextInt()
}
}
Summary
Mocking libraries such as MockK and Mockito (Mockito-Kotlin) gives us several ways of creating stubs. The most common choice is doReturn
or returns
– stubbed method will be evaluated once. If your test design requires evaluating stub each time it’s invoked, use doAnswer{}
or answers
.
Further reading
Kotlin Testing libraries in one place
https://androidpro.io/libraries/
Mockito-Kotlin
https://androidpro.io/using-mockito-in-kotlin-projects/
MockK for suspend function
https://androidpro.io/mocking-suspend-with-mockk/