You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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。最合理的做法是对第三方依赖做一层封装,通过依赖注入实现测试时的替换:

  1. 定义特质抽象第三方方法:
trait PQRClient {
  def test2(input: String): String
}
  1. 实现生产环境的客户端,调用第三方PQR:
class DefaultPQRClient extends PQRClient {
  override def test2(input: String): String = PQR.test2(input)
}
  1. 修改XYZ类,注入PQRClient(默认使用生产实现):
class XYZ(pqrClient: PQRClient = new DefaultPQRClient) {
  def test(): Unit = {
    val ret = pqrClient.test2("abc")
    // ...其他逻辑
  }
}
  1. 测试时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类限制,但它会修改类加载机制,增加测试维护成本:

  1. 引入PowerMock依赖(sbt示例):
libraryDependencies ++= Seq(
  "org.powermock" % "powermock-module-junit4" % "2.0.9" % Test,
  "org.powermock" % "powermock-api-mockito2" % "2.0.9" % Test
)
  1. 编写测试代码:
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单例对象:

  1. 引入ScalaMock依赖(sbt示例):
libraryDependencies += "org.scalamock" %% "scalamock" % "5.2.0" % Test
  1. 编写测试代码:
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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 08:43:17