Scala中Mock无法模拟父类方法?问题咨询
问题分析与解决方案
这个问题的核心是初始化时机不匹配——你的mock规则设置得太晚了!我们先理清楚代码的执行顺序,再看怎么解决:
先明确代码执行流程
你的类结构:
class B { def doStuff() = { // 真实的API调用逻辑 } } class A extends B { val x = doStuff() // 这行在A的构造阶段执行! // ... 其他方法,比如doOtherStuff() }
测试代码的执行顺序:
val a = new A→ 创建A实例,立刻触发构造逻辑,执行x = doStuff(),此时调用的是B中真实的API方法。when(a.doStuff()).thenReturn("stuff")→ 这时候才设置mock规则,但原API调用已经完成了,mock根本没机会拦截构造阶段的调用。
解决方案
根据你的场景,有几种可行的修复方式:
1. 延迟初始化x(最简单的快速修复)
把val x改成lazy val,这样doStuff()会在x第一次被使用时才执行,而不是在构造阶段。这时候你已经设置好mock规则了:
class A extends B { lazy val x = doStuff() // 延迟到首次使用时初始化 // ... } // 测试代码不变,此时mock会生效 class ASpec { val a = new A when(a.doStuff()).thenReturn("stuff") assert(a.doOtherStuff() == "stuff") // 此时x会被初始化,调用mock的doStuff() }
2. 使用依赖注入(更优雅的长期方案)
避免让A继承B,而是把B作为依赖注入到A中。这样你可以直接mock B的实例,传给A,从根源上避免构造时的真实API调用:
// 重构类结构 class B { def doStuff() = { /* 真实API调用 */ } } class A(b: B) { val x = b.doStuff() // 调用传入的B实例的方法 def doOtherStuff() = x } // 测试代码 class ASpec { // 先mock B的实例,设置好规则 val mockB = mock[B] when(mockB.doStuff()).thenReturn("stuff") // 把mock的B传给A,此时构造A时调用的是mock的doStuff() val a = new A(mockB) assert(a.doOtherStuff() == "stuff") // 断言成功 }
这种方式更符合依赖倒置原则,也让测试更灵活。
3. 使用Spy(如果必须保留继承关系)
如果因为某些原因必须让A继承B,可以用Spy包装真实的A实例,但注意还是要配合lazy val,否则构造时的调用还是会执行真实逻辑:
class A extends B { lazy val x = doStuff() // 仍然需要延迟初始化 // ... } class ASpec { val spyA = spy(new A()) // 创建A的spy实例 when(spyA.doStuff()).thenReturn("stuff") assert(spyA.doOtherStuff() == "stuff") }
总结
本质问题是构造阶段的方法调用早于mock规则设置,解决的核心就是让doStuff()的执行时机晚于mock规则的设置,或者通过依赖注入完全控制依赖的实例。
内容的提问来源于stack exchange,提问作者NutellaAddict
相关产品推荐
相关产品推荐

