如何模拟函数内的函数调用?

How to a mock a function call within a function?

我正在为 PlayFramework Scala 应用程序创建单元测试,遇到了一个我需要测试的函数,该函数进行命令行界面调用。这个 cli 调用不能在我们的测试环境中 运行 所以我想模拟它。

class Foo @Inject()(val bar: Bar, val a: A, val b: B...) {
    def testThis(...) = {
        ...
        callCommandLine
        ...
    }
}

class Bar() {
    def callCommandLine(s: String): String = {
        ...
    }
}

下面是我试过的

class FooSpec() {
    "testFoo" in {
        val foo = app.injector.instanceOf[Foo]
        val result = testThis(...)

        val bar = mock[Bar]
        val mockedOutput = "fake cmd line result"
        when(bar.callCommandLine(anyString)).thenReturn(mockedOutput)

        result mustBe mockedOutput
    }
}

我明白为什么我的测试不起作用,但我不知道我需要做什么才能让它起作用。我应该将模拟的 bar class 注入 foo 吗?

假设你的代码实际上是

class Foo @Inject()(val bar: Bar, val a: A, val b: B...) {
    def testThis(...) = {
        ...
        bar.callCommandLine()  // <-- difference here
        ...
    }
}

您面临的障碍是因为您正在使用 "actual" 应用程序及其依赖注入容器来构建 Foo。在 "actual" 应用程序中,Bar 显然绑定到一个实际的 Bar 实例,而不是您在测试中装箱的模拟实例。

要解决此问题,您有两种选择:

  1. 手动创建 Foo 实例:
"testFoo" in {
    val mockedBar = mock[Bar]
    when(mockedBar.callCommandLine(anyString)).thenReturn(...)
    val foo = new Foo(mockedBar, mock[A], mock[B], ...)
    foo.testThis shouldBe "expectedResult"
}

这种方式简单明了,但是完全mock掉了其他依赖(AB等)。在大多数情况下,这是一个可接受的(甚至是可取的)结果,因为它允许独立于依赖项行为测试 Foo 中的代码。

缺点很明显 - 这不是集成测试,所以它涵盖的范围较少(即 AB 行为),并且不测试生产中要使用的实际组件,因为它不以任何方式涉及依赖注入。

就我个人而言,我建议采用这种方式 - 它将创建更多 "independent" 或 "orthogonal" 测试,并允许在它具有的所有依赖项的不同行为下测试 Foo .

  1. 创建一个特定于测试的依赖项注入容器并在那里模拟 Bar
val mockedBar = mock[Bar]
val app = new GuiceApplicationBuilder()
  .overrides(bind[Bar].toInstance(mockedBar))
  .build()

"testFoo" in {
    when(mockedBar.callCommandLine(anyString)).thenReturn(...)
    val foo = app.injector.instanceOf[Foo]
    foo.testThis shouldBe "expectedResult"
}

请注意,此代码段不会在使用后重置模拟,但使用 beforeEach 完成此操作应该是微不足道的。更好的方法是为每个测试创建一个 mockedBar 的新实例,但为了简洁起见,我将在此处省略它。

PlayFramework documentation 中有一节介绍了这个特定用例。

这种方法更像是集成测试 - 除了 Bar 之外的所有依赖项都使用实际实现,具有它带来的所有优点和缺点。