【问题标题】:Is it bad practice to have more than one assertion in a unit test? [closed]在单元测试中拥有多个断言是不好的做法吗? [关闭]
【发布时间】:2010-10-20 05:40:19
【问题描述】:

在一个单元测试中拥有多个断言是不好的做法吗?有关系吗?

【问题讨论】:

标签: unit-testing assert


【解决方案1】:

不,这不是一个坏习惯。如果您正在测试的方法返回一个类,您应该测试应该设置的不同变量。为此,您不妨使用一个单元测试。

但是,如果您在一个单元测试中测试多个功能,那么当它失败时,您就不清楚是哪些功能导致了问题。记住单元测试是你的朋友,所以让他们帮助你。让您可以轻松查看问题所在,以便您进行修复。

【讨论】:

  • -1:我会用不同的“单元测试”测试该类的不同属性。这使得发现错误/问题(未通过测试)变得非常容易
【解决方案2】:

我不认为这是不好的做法。单元测试可以做任何他们想做的事情:断言、记录到文件、向管理层发送侮辱性 SMS 消息等等。

可能的问题是,增加的复杂性可能会改变被测程序的行为,但如果你小心的话,这种情况很少发生,而且无论如何都会被发现。

【讨论】:

  • 一个“单元测试”应该测试 1 个单元,所以尽可能小并且只测试 1 个特定的东西。我只会选择 1 个断言
  • 彼得,这取决于您想要的粒度。您可能需要对每一个可能的失败进行测试(例如一个整数太高或太低,因为两个 单独的 断言)。但是,reduceio ad absurdum 会倾向于对超出范围的 每个 值的断言 :-) 我发现对一些不太细化的问题进行单元测试测试更有用,甚至可能它检查给定值的所有故障模式(例如,太高、太低、不是作为单个测试中的三个单独断言的素数)。我认为没有理由进行大量不同的测试。
【解决方案3】:

有时我每个测试用例只有一个assert,但我想更多时候我有几个assert 语句。

我见过@Arkain 逃避的情况,其中一段非常大的代码只有一个单元测试套件,只有几个测试用例,它们都标记为testCase1testCase2 等,每个测试用例都有数百个断言。更好的是,每个条件通常取决于先前执行的副作用。每当构建失败时,总是在这样的单元测试中,需要相当长的时间来确定问题出在哪里。

但另一个极端是您的问题所暗示的:每个可能的条件都有一个单独的测试用例。根据您要测试的内容,这可能是有道理的,但每个测试用例通常有几个 asserts。

例如,如果您写了java.lang.Integer,您可能会遇到如下情况:

public void testValueOf() {
    assertEquals(1, Integer.valueOf("1").intValue());
    assertEquals(0, Integer.valueOf("0").intValue());
    assertEquals(-1, Integer.valueOf("-1").intValue());
    assertEquals(Integer.MAX_VALUE, Integer.valueOf("2147483647").intValue());
    assertEquals(Integer.MIN_VALUE, Integer.valueOf("-2147483648").intValue());
    ....
}

public void testValueOfRange() {
    assertNumberFormatException("2147483648");
    assertNumberFormatException("-2147483649");
    ...
}

public void testValueOfNotNumbers() {
    assertNumberFormatException("");
    assertNumberFormatException("notanumber");
    ...
}
private void assertNumberFormatException(String numstr) {
    try {
        int number = Integer.valueOf(numstr).intValue();
        fail("Expected NumberFormatException for string \"" + numstr +
             "\" but instead got the number " + number);
    } catch(NumberFormatException e) {
        // expected exception
    }
}

我可以想到一些简单的规则,用于在测试用例中放入多少断言:

  • 取决于先前执行的副作用的assert 不能超过一个。
  • asserts 组合在一起测试相同的功能/特性或其方面——在不需要时不需要多个单元测试用例的开销。
  • 上述任何规则都应被实用性和常识覆盖。您可能不希望有一千个单元测试用例在每个测试用例中包含一个断言(甚至是多个断言),并且您不希望一个测试用例包含数百个 assert 语句。

【讨论】:

  • +1 表示“没有多个断言取决于先前执行的副作用。”
【解决方案4】:

没关系。唯一重要的是您的单元测试将涵盖所有可能的错误。

这是一个“过度思考”的例子。

【讨论】:

    【解决方案5】:

    您的单元测试应该是相当细粒度的。通常,断言越少,您的测试就越有可能针对特定功能而不是在同一测试中混合测试多个功能。这是否意味着所有测试都应该只有一个断言?不,但如果我发现几个断言,我会认为这是一种“测试气味”,可能会在同一个单元测试中测试多个东西。像对待代码异味一样对待这种“气味”,并重构测试以改进它,以便它只测试一个“事物”——即使它需要多个断言。

    例如,我现在正在做一个 MVC 项目,我编写的测试之一是正确的视图是由动作呈现的。如果不同的代码路径可能导致不同的视图,则实际上可能有几个。这就是我将其定义为正确视图的方式:结果是正确的类型并且具有正确的名称。这需要两个断言,但我只测试一件事。

    var result = controller.Action() as ViewResult;
    
    Assert.IsNotNull( result );
    Assert.AreEqual( viewName, result.ViewName );
    

    我可能会对模型做类似的事情,但我不会在与检查视图相同的测试中测试模型是否正确,因为这些是代码行为的不同方面。我可以更改预期的模型或视图,并将其放在单独的测试中,只有那些与该方法的特性相关的测试需要更改。

    【讨论】:

    • 我会将这 2 个断言放在 2 个不同的单元测试中。如有必要,将测试代码重构为方法,这样就不会出现重复代码。
    • 你可以去掉第一个assert,因为如果第二个assert抛出nullreferenceexception,你就知道它是null。
    • @xhafan - 在测试框架中显示效果不佳,在我看来是测试中的错误。
    【解决方案6】:

    放入所有你想要的断言。认真的。

    我尝试断言直到并包括我的测试的具体目标的每一步。

    【讨论】:

    • -1:如果您有 2 个断言一个接一个,则第二个断言依赖于第一个断言。很难在测试用例中发现问题/错误
    • 你知道你可以而且应该在你的断言中加入信息,对吧?这可以准确地告诉您失败的位置。使用 NUnit 作为测试平台,您还可以进行堆栈爬取,因此您可以准确地知道故障在哪里。我不明白你的问题是什么。
    • 小心:测试结束时的多个断言很好(这篇文章的多数意见),但你不会发现很多人认为断言事情在你之前调用您正在测试的方法是个好主意。这些断言应该已经在其他单元测试中。
    【解决方案7】:

    对我来说,在单元测试中有多个断言是很常见的。我通常有一个前置条件的断言,然后是预期的后置条件的断言。

    考虑:

    assert(list.isEmpty());
    
    FetchValues(list);
    
    assert(list.count == expectedItemCount);
    
    AssertValuesMatch(list,expectedValues);
    

    是的,我可以将两个后置条件分成两个测试,但根据 FetchValues 的成本,可能会不必要地减慢整个测试过程。

    【讨论】:

    • 由于断言不相互落后,没关系。但这更像是“自动化功能测试”,而不是“单元测试”。
    【解决方案8】:

    我绝对应该在测试方法中只使用一个断言!使用许多断言可能是您正在测试不止一件事的代码味道。此外,有人可能会在您的测试中添加新的断言而不是编写另一个断言。当第一个断言失败时,您如何理解其他断言如何完成?

    您可能还会发现这篇文章很有趣:https://timetocode.wordpress.com/2016/06/01/zen-of-unit-testing/

    【讨论】:

      猜你喜欢
      • 2010-11-25
      • 2011-01-26
      • 1970-01-01
      • 2013-01-14
      • 1970-01-01
      • 1970-01-01
      • 2014-03-11
      • 2014-03-17
      • 2011-02-18
      相关资源
      最近更新 更多