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

Scala中Mockito Mock Trait方法后子类调用失效问题排查与解决

问题描述

我尝试Mock一个调用外部服务的方法,为排查根因简化了问题场景。我发现可以Mock trait中的方法,但继承该trait的类调用此方法时Mock不生效,这是为什么?该如何解决?

想要Mock的login方法代码如下:

case class User(id: Int, name: String)

trait LoginService {
  def login(name: String, password: String): Option[User] = Some(User(1, "anne"))
}

class OtherService extends LoginService {
  def getId: Int = {
     val getPassword="xx" // 更多待测试逻辑...
     login("Anne", getPassword).get.id
  }
}

Scala测试中使用Mockito进行Mock的代码如下:

class MySpec extends AnyWordSpec with Matchers with MockitoSugar {

  "test3" in {
    val service = mock[LoginService]
    when(service.login("Anne", "xx")) thenReturn Some(User(222, "AA"))

    val other = new OtherService()
    other.getId should be(222)
  }

  "test2" in {
    val service = mock[OtherService]
    when(service.login("Anne", "xx")) thenReturn Some(User(222, "AA"))

    val other=new OtherService()
    other.getId should be(222)
  }

  "test1" in {
    val service = mock[LoginService]
    when(service.login("Anne", "xx")) thenReturn Some(User(222, "AA"))

    service.login("Anne","xx").get.id should be(222)
  }
}

只有test1执行成功,而验证getId方法的test2和test3均失败,为何这两个测试无法模拟出id为222的用户?

依赖配置:

"org.scalatest" %% "scalatest" % "3.2.14"  % Test,
     "org.mockito"  %% "mockito-scala-scalatest" % "1.17.12" % Test,
原因分析
  • test3失败原因:mock的是独立的LoginService实例,但new OtherService()创建的是全新的、继承了LoginService的真实对象,它调用的是自身继承来的login方法,和mock的service实例无关联,不会使用mock返回值。
  • test2失败原因:mock了OtherService类,但new OtherService()创建的是另一个真实的OtherService对象,mock实例和真实对象是完全独立的两个个体,真实对象不会复用mock实例的行为配置。
解决方案

有两种常用解决方式,可根据场景选择:

方式1:依赖注入(推荐)

修改OtherService实现,不再直接继承LoginService,而是将其作为构造参数传入,测试时可轻松替换为mock实例:

// 修改后的业务代码
class OtherService(loginService: LoginService) {
  def getId: Int = {
    val getPassword = "xx"
    loginService.login("Anne", getPassword).get.id
  }
}

对应的测试代码:

class MySpec extends AnyWordSpec with Matchers with MockitoSugar {
  "test with dependency injection" in {
    val mockLoginService = mock[LoginService]
    when(mockLoginService.login("Anne", "xx")) thenReturn Some(User(222, "AA"))

    val otherService = new OtherService(mockLoginService)
    otherService.getId should be(222) // 测试通过
  }
}

方式2:使用Spy进行部分Mock

若不想修改原有类结构,可使用Mockito的spy包装真实的OtherService实例,仅mock指定的login方法:

class MySpec extends AnyWordSpec with Matchers with MockitoSugar {
  "test with spy" in {
    val realService = new OtherService()
    val spyService = spy(realService)
    when(spyService.login("Anne", "xx")) thenReturn Some(User(222, "AA"))

    spyService.getId should be(222) // 测试通过
  }
}

注意:使用spy需谨慎,它会保留真实对象的大部分行为,仅替换指定方法,适合无法修改原有类结构的场景。

内容的提问来源于stack exchange,提问作者MrElephant

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 11:01:35