JUnit5 中的 assertAll 与多个断言

Posted

技术标签:

【中文标题】JUnit5 中的 assertAll 与多个断言【英文标题】:assertAll vs multiple assertions in JUnit5 【发布时间】:2017-04-09 08:51:03 【问题描述】:

是否有任何理由对多个断言进行分组:

public void shouldTellIfPrime()
    Assertions.assertAll(
            () -> assertTrue(isPrime(2)),
            () -> assertFalse(isPrime(4))
    );

而不是这样做:

public void shouldTellIfPrime()
    Assertions.assertTrue(isPrime(2));
    Assertions.assertFalse(isPrime(4));

【问题讨论】:

【参考方案1】:

assertAll 的有趣之处在于它always checks all of the assertions that are passed to it,不管有多少失败。如果全部通过,则一切正常 - 如果至少有一个失败,您将获得所有错误的详细结果(并且就此而言是正确的)。

它最适合用于断言在概念上属于一起的一组属性。你的第一直觉是,“我想把它作为一个人来断言”。

示例

您的具体示例不是assertAll 的最佳用例,因为用素数和非素数检查isPrime 是相互独立的——以至于我建议为此编写两种测试方法。

但假设您有一个简单的类,例如包含字段 citystreetnumber 的地址,并且想要断言这些是您所期望的:

Address address = unitUnderTest.methodUnderTest();
assertEquals("Redwood Shores", address.getCity());
assertEquals("Oracle Parkway", address.getStreet());
assertEquals("500", address.getNumber());

现在,一旦第一个断言失败,您将永远看不到第二个断言的结果,这可能会很烦人。有很多方法可以解决这个问题,JUnit Jupiter 的 assertAll 就是其中之一:

Address address = unitUnderTest.methodUnderTest();
assertAll("Should return address of Oracle's headquarter",
    () -> assertEquals("Redwood Shores", address.getCity()),
    () -> assertEquals("Oracle Parkway", address.getStreet()),
    () -> assertEquals("500", address.getNumber())
);

如果被测方法返回错误的地址,这就是你得到的错误:

org.opentest4j.MultipleFailuresError:
    Should return address of Oracle's headquarter (3 failures)
    expected: <Redwood Shores> but was: <Walldorf>
    expected: <Oracle Parkway> but was: <Dietmar-Hopp-Allee>
    expected: <500> but was: <16>

【讨论】:

但不要滥用!一个单一的测试方法应该始终只测试一个关于生产代码的假设。这就是为什么每个测试方法通常只有一个断言的主要原因。 我同意不滥用它并只测试一个假设,但不同意计算断言有任何价值。这是一个纯粹的语法考虑,没有任何相关性。以我为例:Address:equals 很有可能会准确地测试这些属性,在这种情况下,我可以用一个断言来验证它们。从逻辑上讲,根本没有区别,但突然之间它“只有一个断言”。如果我为班级创建一个 Hamcrest 匹配器,也是如此。 “但不同意计算断言有任何价值” 我不建议“计算”断言。 每个测试方法一个断言是一个经验法则,仅此而已……无论如何,如果您有多个断言,应该问问自己,您的测试是否真的测试了一个单个假设. 我不完全同意“一个测试,一个断言”的经验法则。它假定被测代码运行起来既快速又简单。随着您的测试从低级单元测试扩展到高级集成测试,这个假设不成立。运行一段昂贵的代码并在结果上运行几个廉价的断言比运行昂贵的代码多次并每次测试一件事要高效得多。中间点的断言也有助于调试。只要您清楚地标记断言,使用多个断言就没有问题。【参考方案2】:

根据文档here

断言所有提供的可执行文件都不会抛出 AssertionError。

如果任何提供的 Executable 抛出 AssertionError,所有剩余的 可执行文件仍将被执行,所有失败都将 聚合并在 MultipleFailuresError 中报告。然而,如果一个 可执行文件抛出一个不是 AssertionError 的异常, 执行将立即停止,异常将被重新抛出为 只是被屏蔽为未经检查的异常。

所以主要区别在于 assertAll 将允许所有断言在不中断流程的情况下执行,而其他断言如 assertTrue 并且批次将使用 AssertionError

因此,在您的第一个示例中,无论通过失败,两个断言都将执行,而在第二个示例中,如果第一个断言失败,测试将停止。

是否有任何理由对多个断言进行分组

如果您希望在单元测试中执行所有断言。

【讨论】:

以上是关于JUnit5 中的 assertAll 与多个断言的主要内容,如果未能解决你的问题,请参考以下文章

JUnit 5:如何断言抛出异常?

SpringBoot2---单元测试(Junit5)

SpringBoot——单元测试之JUnit5

SpringBoot——单元测试之JUnit5

SpringBoot——单元测试之JUnit5

JUnit4 & JUnit5 异常断言