【问题标题】:Assert that "nothing happened" when writing unit test在编写单元测试时断言“什么都没有发生”
【发布时间】:2018-02-02 17:51:38
【问题描述】:

在编写单元测试时,有没有一种简单的方法可以确保没有意外发生?

由于可能的副作用列表是无限的,因此添加大量 Assert 以确保在每一步都没有改变似乎是徒劳的,而且它混淆了测试的目的。

我可能错过了一些框架功能或良好实践。
我正在使用 C#7、.net 4.6、MSTest V1。

编辑: 更简单的例子是测试视图模型的设置器,应该发生两件事:值应该改变并且应该引发 PropertyChanged 事件。 这两件事很容易检查,但现在我需要确保其他属性值没有改变,没有引发其他事件,没有触及系统剪贴板......

【问题讨论】:

  • “什么都没发生”具体是什么意思?如果你执行代码'某事'将会发生。你想验证什么没有改变?变量还是?
  • 正如你所说,副作用列表是无限的。所以你应该检查一下,这些情况不会发生。
  • 每个测试应该只检查一件事,所以除了这件事你不关心其他任何事情。我不管Add(1,5)是不是用积分算出来的,只要我得到6就可以了……
  • 如果你发现你需要这样的东西,我猜你没有足够的测试隔离。
  • 有点旁注,但这是使用基于事件的模型的优势之一,例如在 CQRS+ES 中。实体上发生的一切都以事件的形式发生,因此很容易知道是否没有发生任何事情:只需查看是否生成了任何事件!

标签: c# .net unit-testing mstest


【解决方案1】:

您错过了单元测试的重点。它们是“证据”。你不能在逻辑上证明一个否定的断言,所以即使尝试也是没有意义的。

每个单元测试中的断言应该证明期望的行为已经完成。就是这样。

如果我们将问题简化为荒谬,那么每个单元测试都要求我们断言被测函数没有引发热核战争。

单元测试不是您需要执行的唯一一种测试。有功能测试、集成测试、可用性测试等,每一个都有自己的重点。对于单元测试,重点是证明单个函数的预期行为。因此,如果该函数应该完成两件事,只需断言这两件事中的每一件事都发生了,然后继续。

【讨论】:

  • “你不能在逻辑上证明一个否定的断言”本身就是一个否定的断言。所以,如果它是真的,它本身就是无法证明的。我认为您的意思是您无法证明某些东西不存在(例如,在这种情况下是副作用)。
  • 事实上,使用引用透明的编程语言,完全证明这是可能的。这在 Safe Haskell 中是可能的。
【解决方案2】:

确保没有任何“坏”或意外发生的选项之一是确保使用依赖注入和模拟的良好做法:

[Test]
public void TestSomething()
{
    // Arrange
    var barMock = RhinoMocks.MockRepository.GenerateStrictMock<IBar>();
    var foo = new Foo(barMock);
    // Act
    foo.DoSomething();
    // Assert
    ...
}

在上面的例子中,如果Foo 不小心碰到了Bar,这将导致异常(严格模拟)并且测试失败。这种方法可能不适用于所有测试用例,但可以作为其他潜在实践的良好补充。

【讨论】:

  • 我现在不能使用 rhinomock,但他们的 StrictMock 是我正在寻找的东西的一部分。谢谢
【解决方案3】:

对您的编辑的一些补充:

Test Driven Development 中,您只编写代码,它将通过测试,仅此而已。此外,您希望选择最简单的解决方案来实现这一目标。

也就是说,您很可能会从失败的单元测试开始。在您的情况下,您不会在一开始就遇到失败的单元测试。

如果你把它推到极限,当你想检查每个结果时,你必须检查format C:\ 没有在你的应用程序中被调用。您可能想看看KISS-principle 之类的设计原则(保持简单,愚蠢)。

【讨论】:

  • 这对我来说是完美的。我正在使用一种工厂模式,它为给定的情况返回一个服务类。一种情况是 NoOp。我被困在“测试”上,这是一个空操作,而你的“回归基础”方法让我回到了正轨。我失败的单元测试正在调用该方法(方法不存在)。我通过创建方法解决了这个问题,没有别的。测试完成 :) 有时很容易获得隧道视野,只见树木不见森林。
【解决方案4】:

如果“检查没有其他事情发生”的范围是确保模型的状态没有改变,那么问题似乎就是这种情况。

编写一个辅助函数,获取事件之前的模型和事件之后的模型并进行比较。让它返回已更改的属性,然后您可以断言只有那些您打算更新的属性在返回列表中。这种助手是可移植的、可维护的和可重用的

检查模型状态是单元测试的有效应用。

【讨论】:

  • 感谢您的建议。检查模型的变化是否超出预期并不是我唯一关心的问题,但根据 Serge 的建议,这确实应该涵盖大多数情况。然而,使用反射来查找每个字段的通用函数,检查允许更改的参数列表并不是那么简单,我希望有人已经为我做了。
  • 查看这个答案:stackoverflow.com/questions/737151/… 这是一种很好的、​​易于读/写的吐出对象属性的方法
【解决方案5】:

这只有在引用透明的语言(如 Safe Haskell)中才有可能。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-03
    • 1970-01-01
    • 1970-01-01
    • 2011-03-15
    • 2018-04-19
    • 2010-09-08
    相关资源
    最近更新 更多