我目前正在研究Scala项目,并且在测试广泛使用Singleton对象的代码时遇到困难。

我有这样的单身人士:

object SomeSingleton {
  def someFunction(s: String): String = s + "b"
}


在项目中的某处,我可以在要测试的方法中使用它:

  val s = SomeSingleton.someFunction("a")


我开发了新功能,并且我希望SomeSingleton.someFunction在测试中返回某个特定值而没有真正的方法调用,我在Java中使用Powermock做到了这一点,但是我没有找到在Scala中做到这一点的方法。
我有一个想法,我仍然可以做到这一点,但这意味着将原始代码更改为此:

object SomeSingleton extends SomeSingletonClass{}

class SomeSingletonClass {
  // Whole logic from SomeSingleton moves here
  def someFunction(s: String): String = s + "b"
}


现在,我可以将SomeSingleton的用法替换为SomeSingletonClass并对其进行适当的模拟。所以这是我的担忧,该项目规模不小,没有100%进行测试,而且我对Scala还是比较陌生的,所以我不确定,它会导致任何不良后果吗?这是可行的解决方案吗?因此,我不希望我的旧代码在某个随机位置中断。

最佳答案

给定您所拥有的(具有很多单例的遗留代码),我认为您的解决方案可能还可以。我想在这里分享我的一些想法。


是的,引入SomeSingletonClass类将有助于模拟该类,但是缺点是它不再是单例。我可以轻松地执行val newSingleton = new SomeSingletonClass(),瞧,我创建了一个新对象。知道,测试单例的最佳方法是直接测试单例。我想您的单例将调用更多其他单例,并且您不想执行所有这些副作用。但是要正确解决问题,您将需要大量重构代码。
PowerMock是Java中非常强大的模拟工具(可能功能太多)。它可以模拟静态方法/单例的方式是修改字节码。它在大多数情况下都可以使用,但是当它不起作用时,您绝对不知道如何修复它。在我之前在Amazon的工作中,我们曾经广泛使用PowerMock,但是由于它的黑匣子魔力,我们逐渐远离它。帮助我们实现这一目标的一种方法是使用依赖项注入,您可以轻松地在测试中注入模拟对象。
我可以看到您正在处理旧版代码,因此要重写所有内容并不容易(例如,引入依赖项注入,甚至是您尝试在此处尝试的新SomeSingletonClass)。我的建议是,您可能不应该花太多时间为遗留代码编写测试。相反,在编写新代码时,请尝试使其可测试,并根据需要重构旧代码。

09-25 17:13