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

Scala中如何正确测试调用同类方法的Case Class方法?

问题

我有一个表示电话号码的Case Class,它提供了一些在代码其他地方使用的辅助方法,其中一个方法isPossiblyUsable调用了同类中的另外两个方法isUsable和usabilityUnknown。我想对这个方法进行单元测试,出于Python开发背景,我尝试Mock这两个依赖方法,但测试失败,报错显示未调用预期的方法。

Case Class代码

case class PhoneNumber {
  // 类数据和方法
  def isPossiblyUsable(): Boolean = {
    // 两个方法均返回Boolean
    this.isUsable() || this.usabilityUnknown()
  }
}

测试代码

"PhoneNumber.isPossiblyUsable" should "return true when usable" in {
  val m = mock[PhoneNumber]
  (m.isUsable _).expects().returning(true)
  (m.usabilityUnknown _).expects().returning(false)
  assert(m.isPossiblyUsable() == true)
}

错误信息

[info] PhoneNumber.isPossiblyUsable
[info] - should return true when usable *** FAILED ***
[info]   Unexpected call: <mock-1> PhoneNumber.isPossiblyUsable()
[info]   
[info]   Expected:
[info]   inAnyOrder {
[info]     <mock-1> PhoneNumber.isUsable() once (never called - UNSATISFIED)
[info]     <mock-1> PhoneNumber.usabilityUnknown() once (never called - UNSATISFIED)
[info]   }
[info]   
[info]   Actual:
[info]     <mock-1> PhoneNumber.isPossiblyUsable() (Option.scala:201)

解决方案

问题根源

你用mock[PhoneNumber]创建的是完全模拟对象,这个对象的所有方法(包括isPossiblyUsable)都是框架生成的空实现,不会执行原类中的真实逻辑。所以调用m.isPossiblyUsable()时,并不会触发原代码里的this.isUsable() || this.usabilityUnknown()逻辑,自然不会调用你预期的两个方法。

正确实现方式

要测试isPossiblyUsable的逻辑,有两种靠谱的做法:

  1. 使用部分模拟(Partial Mock)
    保留原类isPossiblyUsable的真实实现,仅MockisUsable和usabilityUnknown这两个依赖方法。以Mockito为例,代码可修改为:

    "PhoneNumber.isPossiblyUsable" should "return true when usable" in {
      val phoneNumber = spy(new PhoneNumber()) // spy创建真实实例,允许替换指定方法
      doReturn(true).when(phoneNumber).isUsable()
      doReturn(false).when(phoneNumber).usabilityUnknown()
      assert(phoneNumber.isPossiblyUsable() == true)
    }
    

    spy会生成一个真实的PhoneNumber实例,但允许你覆盖指定方法的返回值,这样isPossiblyUsable会执行原逻辑,并调用被Mock的两个方法。

  2. 直接测试真实实例,通过状态控制依赖方法返回
    如果isUsable和usabilityUnknown的返回值由PhoneNumber的内部状态(比如构造参数)决定,直接创建真实实例并设置对应状态即可:

    "PhoneNumber.isPossiblyUsable" should "return true when usable" in {
      // 假设通过构造参数控制isUsable的返回结果
      val phoneNumber = PhoneNumber(/* 传入使isUsable返回true的参数 */)
      assert(phoneNumber.isPossiblyUsable() == true)
    }
    

    这种方式更符合单元测试的核心原则——测试真实业务逻辑,避免过度依赖Mock。

总结

Scala的Mock框架(如Mockito、ScalaMock)中,mock和spy的职责区分严格:mock是完全模拟,不会执行原类方法;spy(或ScalaMock中的partialMock)才是保留真实逻辑、仅替换指定方法的部分模拟方式。你的Python开发习惯在这里不适用,因为Python的Mock默认行为和Scala框架有差异。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 05:10:54