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

MockK单元测试报Verification failed:方法未被调用问题排查

问题背景

我在开展单元测试,目标是验证类中特定方法是否正确调用了同属该类的其他方法,已移除所有无关冗余代码,核心业务代码如下:

public class Monitor {
   protected void onMessage(String message) {
     Log.d(TAG, "Detected the message");
     changeOptions();
  }

  protected void changeOptions() {
      Log.d("Reached changeOptions() method");
   }
}

对应的测试代码如下:

@RunWith(RobolectricTestRunner::class)
class MonitorTest {

    private lateinit var mymonitor : Monitor

    @MockK private lateinit var mockContext : Context

    @Before
    fun setup() {
        init(this, relaxed = true)
        mockkConstructor(Monitor::class)
        every { anyConstructed<Monitor>().getApplicationContext() } returns mockContext
        mymonitor = Monitor()
        mymonitor.init()

    }

    @Test
    fun test_onMessage() {
        val testString : String = "hello"
        mymonitor.onMessage(testString)

        verify(exactly = 1) {mymonitor.changeOptions()}

    }
}

运行测试时抛出如下错误:

java.lang.AssertionError: Verification failed: call 1 of 1: Monitor(mockkConstructor<Monitor>()).changeOptions() was not called.

Calls to same mock:
1) Monitor(mockkConstructor<Monitor>()).getApplicationContext()
2) Monitor(mockkConstructor<Monitor>()).onMessage("hello")

我确认changeOptions()方法实际已被调用,运行时终端打印了对应日志:

TAG "Detected the message"
TAG "Reached changeOptions() method"

需要定位问题原因并找到修复方案。

故障原因

mockkConstructor只会标记通过Monitor构造函数生成的对象,拦截外部直接调用的方法,但你赋值给mymonitor的本质还是真实的Monitor实例,不是带调用追踪逻辑的代理对象:

  • 你在测试代码里主动从外部调用的getApplicationContext()、onMessage()会被拦截记录,所以会出现在报错的调用列表里
  • 但onMessage()内部调用changeOptions()属于对象内部的自调用,不会走MockK的拦截层,所以虽然方法实际执行打印了日志,但MockK拿不到这次调用的记录,最终报验证失败。
    另外原测试代码的verify行存在多余右括号的语法问题,也需要一并修正。
修复方案

使用spyk创建Monitor的间谍实例,替代直接实例化真实对象+mockkConstructor的方案:间谍对象会完整保留所有真实方法的执行逻辑,同时会记录所有方法的调用轨迹,原生支持verify验证逻辑,是MockK中测试类内部方法调用的标准写法。
修改后的测试代码如下:

@RunWith(RobolectricTestRunner::class)
class MonitorTest {

    private lateinit var mymonitor : Monitor

    @MockK private lateinit var mockContext : Context

    @Before
    fun setup() {
        init(this, relaxed = true)
        // 移除mockkConstructor相关逻辑,创建间谍实例
        mymonitor = spyk(Monitor())
        // 针对间谍实例打桩,返回mock的Context
        every { mymonitor.getApplicationContext() } returns mockContext
        mymonitor.init()
    }

    @Test
    fun test_onMessage() {
        val testString : String = "hello"
        mymonitor.onMessage(testString)
        // 修正原代码多余括号的语法错误,执行验证
        verify(exactly = 1) { mymonitor.changeOptions() }
    }
}

修改后重新运行测试即可正常通过。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 15:00:53