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
相关产品推荐
相关产品推荐

