【问题标题】:Are multiple asserts bad in a unit test? Even if chaining?单元测试中的多个断言是否不好?就算串起来?
【发布时间】:2011-01-26 16:33:16
【问题描述】:

在这个单元测试中检查这么多东西有什么问题吗?:

ActualModel = ActualResult.AssertViewRendered()        // check 1
                          .ForView("Index")            // check 2
                          .WithViewData<List<Page>>(); // check 3

CollectionAssert.AreEqual(Expected, ActualModel);      // check 4

此测试的主要目标是验证返回了正确的视图(检查 2)并且它包含正确的数据(检查 4)。

将其拆分为多个测试会有所收获吗?我都是为了把事情做对,但如果没有实用价值我不会拆散。

我对单元测试很陌生,所以要温柔。

【问题讨论】:

标签: c# unit-testing asp.net-mvc-2 mstest


【解决方案1】:

最好在每个测试中只坚持一个断言以避免Assertion Roulette

如果您需要设置相同的场景来测试基于相同前提的多个条件,最好将设置代码提取到一个共享的辅助方法中,然后编写几个调用该辅助方法的测试。

这确保每个测试用例只测试一件事。

与往常一样,这条规则也有例外,但因为您是单元测试新手,我建议您坚持每个单元测试一个断言规则直到你知道什么时候可以偏离。

【讨论】:

    【解决方案2】:

    只要每个断言都有唯一且可识别的失败消息,您就应该很好,并且会回避任何断言轮盘问题,因为不难判断哪个测试失败。使用常识。

    【讨论】:

    • 拥有多个断言的问题不在于告诉哪个失败了——在大多数情况下,如果没有别的,你会得到行号。问题是您无法查看以后的测试是否也会失败,因此您最终得到的关于该错误的信息较少。在某些情况下,这并不重要 - 但在某些情况下(大多数情况下,根据我的经验,在我学习本课之前)。
    • 是您无法查看以后的测试是否也会失败,但这无关紧要。您一次修复一个断言。假设如果第一个 Assert 失败,下一个 Assert 也会失败,因为之前的条件不满足也是安全的。在我的测试中,我用断言替换了大多数“if”语句。除了分支何时有效(几乎从未如此)。
    • 我不同意。如果您有三个不同的测试并全部运行它们,您将能够看到哪些断言通过了,哪些没有通过。在尝试修复某些问题时,您掌握的信息越多,您的情况就会越好。您可以进行更精确的更改来修复损坏的部分。
    【解决方案3】:

    正如其他人指出的那样,最好在每个测试中坚持一个断言以避免丢失信息 - 如果第一个断言失败,您不知道后面的断言是否也会失败。您必须用更少的信息来解决问题 - 这可能(可能会)更难。

    Roy Osherove 的 Art of Unit Testing 是一个很好的参考 - 如果您想开始使用单元测试,您可能会比从这里开始做得更糟。

    【讨论】:

    • 我觉得那本书非常好。它极大地提高了我的单元测试的质量。虽然大多数关于软件开发的书籍都很厚,但本书包含大约 270 页。这使得将其列入您团队的必读清单非常棒。如果您赶时间,Roy 建议您只阅读第 7 章。
    • 信息保存标准是关键。如果测试以我们被告知哪一行失败的方式执行,则标准只要求每个断言都有自己的行。
    【解决方案4】:

    提醒未来的读者,这个问题及其重复的Is it bad practice to have more than one assertion in a unit test? 具有相反的多数意见,因此请阅读它们并自行决定。

    我的经验是,最有用的测试量不是断言,而是场景——也就是说,对于给定的一组初始条件和方法调用,应该有一个单元测试,并且需要尽可能多的断言断言预期的最终条件。每个断言进行一个单元测试会导致重复设置或曲折的解决方法以避免重复(例如我最近越来越多地看到的可怕的深度嵌套的 rspec 上下文)。它还会增加测试,大大降低您的套件速度。

    【讨论】:

      【解决方案5】:

      我发现了这个问题的不同论据(至少对我自己而言):

      如果测试相同的东西,可以使用多个断言。

      例如,可以这样做:

      Assert.IsNotNull(value);
      Assert.AreEqual(0, value.Count);
      

      为什么? - 因为这两个断言并没有隐藏测试的意图。 如果第一个断言失败,则意味着第二个断言也将失败。 事实上,如果我们删除第一个断言,第二个断言将失败(带有空引用异常 - !!!)无论如何,当 @987654322 @ 一片空白。如果不是这样,那么我们不应该将这两个断言放在一起。

      所以,这是错误的:

      Assert.IsNotNull(value1);
      Assert.IsNotNull(value2);
      

      如上所述,如果第一个断言失败,则没有关于第二个断言的明确指示 - 我们仍然想知道第二个断言会发生什么(即使第一个断言失败强>)。所以,出于这个原因,这两个断言属于两个不同的单元测试。

      结论:放置一个或多个断言,当正确完成,成为偏好问题 - 我们是否希望在测试结果中看到断言异常,或者我们是否希望看到其他一些异常在特定情况下也是如此。

      【讨论】:

        【解决方案6】:

        我知道这是一个老问题,但我想我会补充一点。

        通常我会在每个测试用例中使用尽可能少的断言。通常可以编写测试来节省大量不同的断言。

        假设我有一个方法的单元测试,该方法从名字的组成部分创建一个称呼(例如史密斯先生)。我想用大量不同的场景来检查这一点,没有必要对每个场景进行单独的测试。

        给定以下代码,有许多不同的断言。当它们失败时,您可以一次修复它们,直到断言停止。

        Assert.AreEqual("Mr Smith", GetSalutation("Mr", "J", "Smith"));
        Assert.AreEqual("Mr Smith", GetSalutation("Mr", "John", "Smith"));
        Assert.AreEqual("Sir/Madam", GetSalutation("", "John", "Smith"));
        Assert.AreEqual("Sir/Madam", GetSalutation("", "J", "Smith"));
        

        另一种方法是记录问题并在最后声明。

        int errorCount = 0;
        string result;
        
        result = GetSalutation("Mr", "J", "Smith");
        if (result == "Mr Smith")
            errorCount++;
        
        result = GetSalutation("Mr", "John", "Smith");
        if (result == "Mr Smith")
            errorCount++;
        
        Assert.AreEqual(0, errorCount);
        

        在现实世界的情况下,我可能会添加一些 Trace 命令来将失败的单个测试的详细信息写入输出窗口

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2010-10-20
          • 2011-02-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多