Return vs Answer article cover

Stubbing with doAnswer vs doReturn – what’s the difference?

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?

  1. Create mock with Mockito-Kotlin
  2. stub random() method from RandomNumberProvider with doReturn
  3. return Random.nextInt()
  4. Use Kotlin built-in method repeat
  5. Inside repeat block invoke stubbed method 10 times.

Run this test and see results:

just repeat 10 times with doReturn

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
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:

  1. Create mock with Mockito-Kotlin
  2. stub random() method from RandomNumberProvider with doAnswer
  3. in doAnswer create function returning Random.nextInt()
  4. Use Kotlin built-in method repeat
  5. 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 {
      every { random() } returns Random.nextInt()
    }

    repeat(10) {
      println("Return " + timeProvider.random())
    }
  }

  @Test
  fun `it should answer`() {
    val timeProvider = mockk {
      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://kotlintesting.com/libraries/

Mockito-Kotlin
https://kotlintesting.com/using-mockito-in-kotlin-projects/

MockK for suspend function
https://kotlintesting.com/mocking-suspend-with-mockk/

Leave a Comment