Scala单元测试:如何Mock类内部的类成员方法?
嘿,这个问题我之前也碰到过!核心问题是你现在的类A里,b是private val且直接在内部new出来的,你测试里mock的那个B和类A实际用的完全是两个对象,所以你的mock配置根本起不了作用。咱们分两种情况来解决:
推荐方案:重构类A,支持依赖注入
最规范的做法是让类A的依赖(也就是B的实例)可以被外部传入,这样测试时就能轻松替换成mock对象。我们可以给类A加一个带默认参数的构造函数,这样既不影响原有代码的调用,又能满足测试需求:
class A(b: B = new B()) { def doSomething(): Unit = { b.someMethod() } }
这样一来,正常业务代码里直接new A()和原来的行为完全一致,而测试时我们可以传入mock的B。
对应的单元测试代码
用你正在用的MockitoSugar,测试代码可以这么写:
import org.scalatest.funsuite.AnyFunSuite import org.mockito.MockitoSugar._ class ATest extends AnyFunSuite { test("doSomething should invoke B's someMethod") { // 1. 创建mock的B实例 val mockB = mock[B] // 2. 初始化待测试的A,传入mockB val classToTest = new A(mockB) // 3. 执行要测试的方法 classToTest.doSomething() // 4. 验证mockB的someMethod是否被调用了一次 verify(mockB).someMethod() } }
注意:如果someMethod是返回Unit的方法,Mockito不需要额外配置thenReturn,直接验证调用即可。
不推荐的方案:用反射替换私有成员(万不得已才用)
如果因为某些限制你不能修改原类A的代码,那只能用反射的方式强行替换私有成员b。这种方式比较hacky,后续类结构变化很容易导致测试失败,所以尽量不用。这里用PowerMock来实现:
import org.scalatest.funsuite.AnyFunSuite import org.mockito.MockitoSugar._ import org.powermock.api.mockito.PowerMockito import org.powermock.core.classloader.annotations.PrepareForTest import org.junit.runner.RunWith import org.powermock.modules.junit4.PowerMockRunner @RunWith(classOf[PowerMockRunner]) @PrepareForTest(Array(classOf[A])) class ATest extends AnyFunSuite { test("doSomething should call B's someMethod via reflection") { val mockB = mock[B] val classToTest = new A() // 反射替换A类中的私有成员b PowerMockito.field(classOf[A], "b").set(classToTest, mockB) classToTest.doSomething() // 验证调用 verify(mockB).someMethod() } }
总之,优先选择重构类A做依赖注入的方案,代码更清晰也更易维护。
内容的提问来源于stack exchange,提问作者tim tim
相关产品推荐
相关产品推荐

