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

sam*_*rko 6 unit-testing scala mocking scalatest scalamock

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

package org.samidarko.actors

import org.samidarko.helpers.Lib

class Monitoring extends Actor {

  override def receive: Receive = {
    case Tick =>
       Lib.sendNotification()
    }
}
Run Code Online (Sandbox Code Playgroud)

有没有办法像使用nodejs的 proxyquire一样从ScalaTest模拟/存根Lib

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

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

class Monitoring(lib: Lib) extends Actor {
Run Code Online (Sandbox Code Playgroud)

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

编辑:

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

我正在传递Libas 参数并使用mockito 进行模拟,它使代码更易于测试和维护,而不是隐藏作用域。

Xav*_*hot 6

此答案仅使用 scalatest,不会影响源代码:

基本解决方案:

假设您有这个 src 类(您要测试并且要为其模拟依赖项的类):

package com.my.code

import com.lib.LibHelper

class MyClass() {
  def myFunction(): String = LibHelper.help()
}
Run Code Online (Sandbox Code Playgroud)

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

package com.lib

object LibHelper {
  def help(): String = "hello world"
}
Run Code Online (Sandbox Code Playgroud)

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

package com.lib

object LibHelper {
  def help(): String = "hello world - overriden"
}
Run Code Online (Sandbox Code Playgroud)

这样你就可以用通常的方式测试你的代码:

package com.my.code

import org.scalatest.FunSuite

class MyClassTest extends FunSuite {
  test("my_test") {
    assert(new MyClass().myFunction() === "hello world - overriden")
  }
}
Run Code Online (Sandbox Code Playgroud)

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

以前的代码清晰简单,但 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"
    }
}
Run Code Online (Sandbox Code Playgroud)

而 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")
  }
}
Run Code Online (Sandbox Code Playgroud)

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

parallelExecution in Test := false
Run Code Online (Sandbox Code Playgroud)