You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何MockK无法验证Micronaut Meter计数器已递增?

Micrometer MeterRegistry无标签计数器测试失败原因及解决方法

问题背景

我需要测试代码是否正确更新了Micronaut(4.7.5)的指标,被测逻辑会接收MeterRegistry实例并调用其方法。测试代码如下:

import io.micrometer.core.instrument.Counter
import io.micrometer.core.instrument.MeterRegistry
import io.micrometer.core.instrument.Tag
import io.micrometer.core.instrument.Tags
import io.mockk.*
import org.junit.jupiter.api.Test

fun MeterRegistry.countIt(): Counter = counter("my-counter")

fun MeterRegistry.countTagged(tag: String): Counter = counter("other-counter", Tags.of(Tag.of("tag", tag)))

class RegistryTest {
    @Test
    fun `test counter`() {
        val registry: MeterRegistry = mockk(relaxed = true)
        registry.countIt().increment()
        verify {  // 测试失败
            registry.countIt().increment()
        }
    }

    @Test
    fun `test counter with tag`() {
        val registry: MeterRegistry = mockk(relaxed = true)
        registry.countTagged("tag").increment()
        verify {  // 测试通过
            registry.countTagged("tag").increment()
        }
    }
}

第一个测试执行时会报错:

Verification failed: call 2 of 2: Counter(child of #4#6).increment()) was not called

第一个测试始终失败,第二个却能正常通过,请问这是什么原因?该如何正确验证第一个无标签指标的递增操作?


原因分析

  • 无标签计数器的问题:当调用仅传名称的counter("my-counter")时,relaxed = true创建的MockK宽松mock,每次调用都会生成全新的Counter实例。实际执行时用的是第一次调用registry.countIt()返回的实例,而verify块里再次调用registry.countIt()时,MockK又生成了第二个实例,验证自然找不到对应调用记录。
  • 带标签计数器正常的原因:带标签的counter调用会根据传入的标签参数做匹配,MockK会复用同一个Counter实例,两次调用registry.countTagged("tag")返回的是同一个对象,所以验证能命中。

解决方法

方案1:提前存根固定返回实例

手动创建Counter的mock对象,指定registry.counter("my-counter")始终返回这个固定实例,确保两次调用拿到的是同一个对象:

@Test
fun `test counter`() {
    val registry: MeterRegistry = mockk()
    val counterMock: Counter = mockk()
    // 存根指定方法的返回值
    every { registry.counter("my-counter") } returns counterMock
    
    registry.countIt().increment()
    // 直接验证mock实例的调用
    verify { counterMock.increment() }
}

方案2:捕获实例后验证

先调用方法获取Counter实例,后续直接验证这个实例的increment调用,避免二次调用生成新实例:

@Test
fun `test counter`() {
    val registry: MeterRegistry = mockk(relaxed = true)
    // 先获取实例
    val counter = registry.countIt()
    counter.increment()
    
    // 验证该实例的increment调用
    verify { counter.increment() }
}

内容的提问来源于stack exchange,提问作者Robert

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 05:39:50