如何创建不调用底层对象构造函数的 ScalaMock 存根?

How do you create a ScalaMock stub that doesn't call the constructor of the underlying object?

考虑以下示例 Scala class 和单元测试:

class BrokenClass(s: String) {
  private val len = s.length
  def length(): Int = len
}

class BrokenTest extends FlatSpec with Matchers with MockFactory {

  "A BrokenClass" should "stub correctly" in {
    val stubThing = stub[BrokenClass]
    (stubThing.length _) when () returns (10)
    stubThing.length should equal (10)
  }

}

在旧版本的 ScalaMock 中,此代码可以工作。使用 Scala 2.12 和 ScalaMock 3.6,我得到一个 NullPointerException,因为即使我正在创建一个存根,它仍然调用 BrokenClass 构造函数的 "s.length" 行。所以它试图取消引用 "s",它是空的,因为我没有向它传递任何东西,因为我想要的只是一个存根,当调用特定方法时 returns 一个特定值。

有没有办法在不尝试调用对象的构造函数的情况下创建存根?为什么这在旧版本中有效?

ScalaMock 使用宏定义生成子类。 该宏在编译器 运行.

期间得到 expanded/evaluated

由于 mock 是子类,因此将调用超类的构造函数 - 无一例外。 您也许可以使用一些 cglib 魔法来解决这个问题,但这不是我熟悉的东西。

所以这在旧的 ScalaMock 版本中可能是可行的,但是这个功能不会很快在当前实现中恢复。

另一种选择是自己实际子类化这个东西并模拟子类

class NotSoBrokenClass extends BrokenClass("")
...
val nsb = mock[NotSoBrokenClass]
...

这在某些情况下有效,但如果构造函数依赖于非最终方法调用,您也会看到有趣的行为(例如 NPE)。