是否可以在 Scala 中模拟/覆盖依赖项/导入?



我有一些代码看起来像这样:

package org.samidarko.actors
import org.samidarko.helpers.Lib
class Monitoring extends Actor {
override def receive: Receive = {
case Tick =>
Lib.sendNotification()
}
}

有没有办法从ScalaTest模拟/存根Lib,就像使用 nodejs 的代理一样?

我读到我可以使用依赖注入,但我宁愿不这样做

我唯一的选择是将我的库作为类参数传递吗?

class Monitoring(lib: Lib) extends Actor {

有什么建议可以使其更易于测试吗?谢谢

编辑:

Xavier Guihot的回答是一种有趣的问题方法,但我选择更改代码以进行测试。

我正在将Lib作为参数传递,并且我正在用 mockito 进行嘲笑,它使代码比阴影范围更容易测试和维护。

这个答案只使用 scalatest ,不会影响源代码:

基本解决方案:

假设你有这个 src 类(你想要测试的那个,你想模拟它的依赖关系):

package com.my.code
import com.lib.LibHelper
class MyClass() {
def myFunction(): String = LibHelper.help()
}

以及这个库依赖项(你想在测试 MyClass 时模拟/覆盖):

package com.lib
object LibHelper {
def help(): String = "hello world"
}

这个想法是在测试文件夹中创建一个类,该类将覆盖/隐藏库。该类将具有与您要模拟的类相同的名称和相同的包。在src/test/scala/com/external/lib中,您可以创建包含以下代码的LibHelper.scala

package com.lib
object LibHelper {
def help(): String = "hello world - overriden"
}

通过这种方式,您可以按常规方式测试代码:

package com.my.code
import org.scalatest.FunSuite
class MyClassTest extends FunSuite {
test("my_test") {
assert(new MyClass().myFunction() === "hello world - overriden")
}
}

改进的解决方案,允许为每个测试设置模拟的行为:

以前的代码清晰而简单,但 LibHelper 的模拟行为对于所有测试都是相同的。人们可能希望有一个 LibHelper 的方法产生不同的输出。因此,我们可以考虑在 LibHelper 中设置一个可变变量,并在每次测试之前更新变量,以便设置 LibHelper 的所需行为。(这仅在 LibHelper 是一个对象时才有效)

影子 LibHelper(src/test/scala/com/external/lib 中的那个)应该替换为:

package com.lib
object LibHelper {
var testName = "test_1"
def help(): String =
testName match {
case "test_1" => "hello world - overriden - test 1"
case "test_2" => "hello world - overriden - test 2"
}
}

而 scalatest 类应该变成:

package com.my.code
import com.lib.LibHelper
import org.scalatest.FunSuite
class MyClassTest extends FunSuite {
test("test_1") {
LibHelper.testName = "test_1"
assert(new MyClass().myFunction() === "hello world - overriden - test 1")
}
test("test_2") {
LibHelper.testName = "test_2"
assert(new MyClass().myFunction() === "hello world - overriden - test 2")
}
}

非常重要的精度,因为我们使用的是全局变量,因此必须强制 scalatest 按顺序(而不是并行)运行测试。关联的 scalatest 选项(包含在 build.sbt 中)是:

parallelExecution in Test := false

不是一个完整的答案(因为我不太了解AOP),但为了让你朝着正确的方向前进,这可以通过名为AspectJ的Java lib来实现:

https://blog.jayway.com/2007/02/16/static-mock-using-aspectj/

https://www.cakesolutions.net/teamblogs/2013/08/07/aspectj-with-akka-scala

伪代码示例(不赘述):

class Mock extends MockAspect {
@Pointcut("execution (* org.samidarko.helpers.Lib.sendNotification(..))")
def intercept() {...}
}

此方法的低级基础知识是动态代理:https://dzone.com/articles/java-dynamic-proxy。但是,您也可以模拟静态方法(也许您必须在模式中添加单词static)。

相关内容

  • 没有找到相关文章

最新更新