2

首先,我是单元测试的初学者。对于我的测试,我想使用 NSubstitute,所以我阅读了网站上的教程以及 Richard Banks 的模拟比较。它们都在针对接口进行测试,而不是针对类进行测试。声明是“通常这个[替换]类型将是一个接口,但你也可以在紧急情况下替换类。”

现在我想知道针对接口进行测试的目的。这是来自 NSubstitute 网站的示例界面(请注意,我已经在 VB.net 中转换了 C# 代码):

Public Interface ICalculator
    Function Add(a As Double, b As Double) As Double
    Property Mode As String
    Event PoweringUp As EventHandler
End Interface

这是来自网站的单元测试(在 NUnit-Framework 下):

<Test>
Sub ReturnValue_For_Methods()

    Dim calculator = Substitute.For(Of ICalculator)()
    calculator.Add(1, 2).Returns(3)

    Assert.AreEqual(calculator.Add(1, 2), 3)

End Sub

好的,这行得通,单元测试将成功执行。但是这有什么意义呢?这不测试任何代码。Add -Method 可能有任何错误,在针对接口进行测试时不会检测到 - 如下所示:

Public Class Calculator
    Implements ICalculator

    Public Function Add(a As Double, b As Double) As Double Implements ICalculator.Add
        Return 1 / 0
    End Function

    ...

End Class

Add -Method执行除以零,因此单元测试应该失败 - 但由于针对接口 ICalculator 进行测试,测试成功。

你能帮我理解吗?它有什么意义,不是测试代码而是测试接口?

在此先感谢迈克尔

4

1 回答 1

0

模拟背后的想法是将我们正在测试的类与其依赖项隔离开来。所以我们不模拟我们正在测试的类,在这种情况下Calculator,我们ICalculator在测试一个使用ICalculator.

一个小例子是当我们想要测试某物如何与数据库交互时,但我们不想使用真实的数据库进行一些快速测试。(请原谅 C#。)

[Test]
public void SaveTodoItemToDatabase() {
  var substituteDb = Substitute.For<IDatabase>();
  var todoScreen = new TodoViewModel(substituteDb);

  todoScreen.Item = "Read StackOverflow";
  todoScreen.CurrentUser = "Anna";
  todoScreen.Save();

  substituteDb.Received().SaveTodo("Read StackOverflow", "Anna");
}

这里的想法是我们将TodoViewModel保存到数据库的细节与细节分开。我们不想担心配置数据库,获取连接字符串,或者之前测试运行的数据会干扰未来的测试运行。使用真实数据库进行测试可能非常有价值,但在某些情况下,我们只想测试较小的功能单元。模拟是这样做的一种方式。

对于真正的应用程序,我们将创建一个TodoViewModel具有实际实现的IDatabase,并且如果该实现遵循接口的预期契约,那么我们可以合理地期望它会起作用。

希望这可以帮助。


更新以回应评论

测试TodoViewModel假设了IDatabase作品的实现,因此我们可以专注于该类的逻辑。这意味着我们可能需要一组单独的测试来实现IDatabase. 假设我们有一个SqlServerDb实现,那么我们可以进行一些测试(可能针对一个真实的数据库)来检查它是否符合它的承诺。在这些测试中,我们将不再模拟数据库接口,因为这就是我们正在测试的。

我们可以做的另一件事是进行“合同测试”,我们可以将其应用于任何IDatabase实现。例如,我们可以有一个测试,说明任何实现,保存一个项目然后再次加载它应该返回相同的项目。然后我们可以针对所有实现运行这些测试SqlDbInMemoryDb等等FileDb。通过这种方式,我们可以陈述我们对我们正在模拟的依赖项的假设,然后检查实际实现是否符合我们的假设。

于 2014-04-30T10:16:11.610 回答