Mohammad Esteki
Mohammad Esteki

Reputation: 11

How to properly verify extension function calls with Mockito in Kotlin?

I'm trying to verify calls to a Kotlin extension function using Mockito, but I'm encountering an issue where the test is counting internal property accesses rather than the actual function call.

Here's my simplified test code:

import org.junit.jupiter.api.Test
import org.mockito.kotlin.spy
import org.mockito.kotlin.times
import org.mockito.kotlin.verify

class MockitoTest {
  class Amount(val value: Int)

  fun Amount.validate() {
    require(this.value > 0) { "Amount should be greater than 0" }
  }

  @Test
  fun `spy the validate calls`() {
    // given
    val spyAmount = spy(Amount(1))

    // when
    spyAmount.validate()

    val value = spyAmount.value

    // verify
    verify(spyAmount, times(1)).validate()
  }
}

When I run this test, I get the following error:

amount.getValue();
Wanted 1 time:
-> at MockitoTest$Amount.getValue(MockitoTest.kt:7)
But was 2 times:
-> at MockitoTest.validate(MockitoTest.kt:10)
-> at MockitoTest.spy the validate calls(MockitoTest.kt:21)

The issue seems to be that Mockito is tracking the internal getValue() call that happens inside the validate() extension function, and then again when I directly access the property. However, I'm trying to verify the actual calls to the validate() extension function itself.

Interestingly, if I move the validate() function into the Amount class like this:

class Amount(val value: Int) {
  fun validate() {
    require(this.value > 0) { "Amount should be greater than 0" }
  }
}

The test passes without any issues.

How can I properly verify that an extension function like validate() is called exactly once without Mockito conflating it with the property accesses that happen inside the function?

Upvotes: 1

Views: 45

Answers (1)

Lunivore
Lunivore

Reputation: 17677

Extension methods don't really exist on the class that you're trying to mock; they're just syntactical sugar that make the code much easier to use but obviously not so easy to test.

I generally try to think about the value that my extension functions are providing, and see if I can find a way to verify that that value was provided.

For instance, maybe the act of getting the value is expensive and that's why you only want to call the validate() method once. In which case, you could check that you only get the value once (twice because you're calling it in the test too); or just put some performance tests in place instead - no mocking required.

Or maybe you just want to check that it stops invalid amounts from being used. In that case, setting up an invalid amount and checking that the exception is thrown is the right thing to do - and you might not require mocking to do that either.

Rather than thinking about how you verify the extension method, think about how to verify the behaviour that provides value instead.

If you absolutely have to mock something to test the valuable behaviour of an extension method, it will have to be the calls to the real object. If you're working with something more complex than this (for instance, we have this same issue with Microsoft's logging in C#) you can make the mocks strict temporarily and use that to quickly find out what the actual calls are.

And if the extension method is on a class or interface that you actually own and control, please consider not using extension methods, and adding the method to the class directly as you've demonstrated here. It's really hard to test it otherwise.

Upvotes: 2

Related Questions