JUnit测试中使用Mockk库Mock除指定方法外的所有类方法
问题根因
你的测试无法生效有两个核心原因:
mockk()创建的是全量Mock对象,所有方法默认不会执行真实逻辑,你未单独配置的method1()本身也被Mock拦截,不会执行内部的分支判断逻辑,自然不会触发method2/method3的调用。- 你定义的
Parent类中method1、method2、method3均未添加open修饰,Kotlin默认方法为final类型,Mockk默认基于子类重写实现方法拦截,final方法无法被重写拦截,哪怕使用Spy也会出现Mock规则不生效的问题。
实现方案
第一步:调整待测试类的可访问性
如果可以修改源码,给需要拦截/执行真实逻辑的方法添加open修饰,允许Mockk重写拦截:
open class Parent() { var condition = true open fun method1() { if (condition) { method2("bum") } else { method3(41) } } open fun method2(someStr: String): String { // 复杂业务逻辑 return "some result" } open fun method3(someInt: Int): Int { // 复杂业务逻辑 return 42 } } class Child: Parent() { }
如果你无法修改待测试类源码(比如依赖的第三方类),只需要在测试依赖中引入
mockk-agent-jvm依赖,Mockk会通过字节码增强机制自动拦截final类和final方法,不需要修改原类代码即可实现后续的Mock/Spy逻辑。
第二步:使用Spy对象代替全量Mock对象
spyk()创建的是部分Mock(Spy)对象,默认所有方法都会执行真实逻辑,只有你通过every手动指定Mock规则的方法才会返回Mock结果,正好匹配你“保留method1真实执行逻辑,Mock method2、method3返回值”的需求。调整后的测试代码如下:
@Test fun `when condition is true, it should call method2`() { clearAllMocks() // 用spyk替代mockk创建实例,默认走真实方法逻辑 val child: Child = spyk() child.condition = true // 仅给需要Mock的method2、method3配置返回规则 every { child.method2(any()) } returns "something" every { child.method3(any()) } returns 11 // 此处调用method1会执行真实逻辑 child.method1() verify(exactly = 1) { child.method2(any()) } // 可选校验:condition为true时不会触发method3调用 verify(exactly = 0) { child.method3(any()) } }
补充说明
不推荐在全量mockk()实例上通过every { xxx } answers { callOriginal() }的方式逐个配置要保留真实逻辑的方法,这种方式需要手动给所有非Mock方法配置规则,维护成本高,容易漏配导致测试逻辑不符合预期,spyk()是处理这类部分Mock场景的标准方案。
内容的提问来源于stack exchange,提问作者Alexey Simchenko
相关产品推荐
相关产品推荐

