最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

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

programmeradmin0浏览0评论

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 .junit.jupiter.api.Test
import .mockito.kotlin.spy
import .mockito.kotlin.times
import .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?

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 .junit.jupiter.api.Test
import .mockito.kotlin.spy
import .mockito.kotlin.times
import .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?

Share Improve this question edited Mar 2 at 10:52 Mohammad Esteki asked Mar 2 at 10:37 Mohammad EstekiMohammad Esteki 133 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 2

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.

发布评论

评论列表(0)

  1. 暂无评论