如何使用Mockk测试Spy的init块是否调用指定方法
问题原因
当你调用spyk<Example>()时,MockK的执行逻辑是:先调用真实构造函数创建Example实例(此时init块立即执行,调用了test()),之后才将这个实例包装成Spy代理。也就是说,init块里的test()调用发生在Spy代理生效之前,完全绕过了MockK的调用拦截器,所以verify无法检测到这次调用。
第一个测试中,init块触发的test()没被统计,只有testWrapper()触发的那一次被记录,因此verify(exactly = 1)能通过;而实际test()执行了两次,但只有代理生效后的那次被计数。
解决方案
方案1:使用@SpyK注解初始化Spy
通过MockKAnnotations配合@SpyK注解,MockK会在对象初始化之前就创建好代理,这样init块中的方法调用会被代理捕获并记录。
修改后的测试代码:
class ExampleTest { @SpyK(recordPrivateCalls = true) lateinit var instance: Example @BeforeEach fun setup() { MockKAnnotations.init(this) } @Test fun `testWrapper should call test`() { instance.testWrapper() verify(exactly = 2) { instance.test() } // init调用1次 + testWrapper调用1次 } @Test fun `init should call test`() { verify(exactly = 1) { instance.test() } } }
方案2:手动控制初始化时机(无需修改原代码)
用mockk创建对象并指定默认调用真实方法,再通过反射触发构造过程,确保调用被MockK拦截:
class ExampleTest { @Test fun `init should call test`() { val instance = mockk<Example>(relaxed = true) { every { anyConstructed<Example>() } answers { callRealMethod() } } Example::class.java.getDeclaredConstructor().newInstance() verify(exactly = 1) { instance.test() } } }
方案3:重构原代码(推荐)
如果允许修改业务代码,将init块逻辑提取到单独方法,让测试更直观:
class Example { init { initLogic() } private fun initLogic() { test() } fun testWrapper() = test() fun test() = println("test was called!") }
测试时可以直接验证私有方法或目标方法:
@Test fun `init should trigger test via initLogic`() { val instance = spyk<Example>(recordPrivateCalls = true) // 验证私有初始化方法被调用 verifyPrivate(instance) { invoke("initLogic") } // 或者直接验证test()的调用次数 verify(exactly = 1) { instance.test() } }
内容的提问来源于stack exchange,提问作者Bart Bos
相关产品推荐
相关产品推荐

