Scala中Mock第三方Object出现Cannot mock/spy class PQR错误的解决方法
问题
Scala类调用第三方库的object PQR方法,单元测试时用Mockito Mock该方法报错:
Cannot mock/spy class PQR
Mockito cannot mock/spy because :
- final class
org.mockito.exceptions.base.MockitoException:
Cannot mock/spy class PQR
Mockito cannot mock/spy because :- final class
相关代码:
class XYZ { def test() : Unit = { val ret = PQR.test2("abc") // 第三方依赖,PQR是object // ...其他逻辑 } } class XYZSpec extends AnyFlatSpec with Matchers with MockitoSugar { it should "create object" in { val pqr = mock[PQR.type] when(pqr.test2(any[String])) thenReturn "def" } }
解决方案
方法一:依赖注入+封装第三方调用(推荐)
Scala的object是单例且默认被标记为final类,Mockito无法直接Mock。最合理的做法是对第三方依赖做一层封装,通过依赖注入实现测试时的替换:
- 定义特质抽象第三方方法:
trait PQRClient { def test2(input: String): String }
- 实现生产环境的客户端,调用第三方
PQR:
class DefaultPQRClient extends PQRClient { override def test2(input: String): String = PQR.test2(input) }
- 修改
XYZ类,注入PQRClient(默认使用生产实现):
class XYZ(pqrClient: PQRClient = new DefaultPQRClient) { def test(): Unit = { val ret = pqrClient.test2("abc") // ...其他逻辑 } }
- 测试时Mock这个特质:
class XYZSpec extends AnyFlatSpec with Matchers with MockitoSugar { it should "execute test logic correctly" in { val mockPqrClient = mock[PQRClient] when(mockPqrClient.test2(any[String])).thenReturn("def") val xyz = new XYZ(mockPqrClient) xyz.test() verify(mockPqrClient).test2("abc") } }
方法二:使用PowerMock(侵入性强,不推荐)
如果无法修改原代码结构,可借助PowerMock绕过final类限制,但它会修改类加载机制,增加测试维护成本:
- 引入PowerMock依赖(sbt示例):
libraryDependencies ++= Seq( "org.powermock" % "powermock-module-junit4" % "2.0.9" % Test, "org.powermock" % "powermock-api-mockito2" % "2.0.9" % Test )
- 编写测试代码:
import org.powermock.api.mockito.PowerMockito import org.powermock.core.classloader.annotations.PrepareForTest import org.scalatestplus.junit.JUnitSuite import org.junit.runner.RunWith import org.powermock.modules.junit4.PowerMockRunner @RunWith(classOf[PowerMockRunner]) @PrepareForTest(Array(classOf[PQR])) class XYZSpec extends JUnitSuite with Matchers with MockitoSugar { it should "mock PQR object" in { PowerMockito.mockStatic(classOf[PQR]) when(PQR.test2(any[String])).thenReturn("def") val xyz = new XYZ() xyz.test() PowerMockito.verifyStatic(classOf[PQR]) PQR.test2("abc") } }
方法三:用ScalaMock替代Mockito
ScalaMock对Scala语言特性(如object、特质)支持更友好,可直接Mock单例对象:
- 引入ScalaMock依赖(sbt示例):
libraryDependencies += "org.scalamock" %% "scalamock" % "5.2.0" % Test
- 编写测试代码:
import org.scalamock.scalatest.MockFactory class XYZSpec extends AnyFlatSpec with Matchers with MockFactory { it should "mock PQR object correctly" in { val mockPQR = mockObject[PQR] (mockPQR.test2 _).expects("abc").returning("def") val xyz = new XYZ() xyz.test() } }
内容的提问来源于stack exchange,提问作者BigDataLearner
相关产品推荐
相关产品推荐

