在同一个 Spock 测试中执行所有断言,即使其中一个失败

Executing all assertions in the same Spock test, even if one of them fails

我正在尝试在运行 when-then-where 形式的多个测试用例的单个 Spock 方法的上下文中验证两个不同的输出。为此,我在 then 块中使用了两个断言,如下例所示:

import spock.lang.*

@Unroll
class ExampleSpec extends Specification {

     def "Authentication test with empty credentials"() {
         when:
         def reportedErrorMessage, reportedErrorCode
         (reportedErrorMessage, reportedErrorCode) = userAuthentication(name, password)

         then:
         reportedErrorMessage == expectedErrorMessage
         reportedErrorCode    == expectedErrorCode

         where:
         name | password || expectedErrorMessage | expectedErrorCode
         ' '  | null     || 'Empty credentials!' | 10003
         ' '  | ' '      || 'Empty credentials!' | 10003
     }
}

该代码是一个示例,其中设计要求是如果 namepassword' 'null,那么我应该总是期望完全相同 expectedErrorMessage = 'Empty credentials!'expectedErrorCode = 10003。如果出于某种原因(大概是因为源代码中的错误)我得到 expectedErrorMessage = Empty!(或 'Empty credentials!' 以外的任何其他内容)和 expectedErrorCode = 10001(或 1003 以外的任何其他内容), 这将不能满足上述要求。

问题是,如果两个断言在同一个测试中都失败了,我只会收到第一个断言的失败消息(此处为reportedErrorMessage)。是否可以在同一测试中获得所有失败断言的通知?

这里有一段代码在没有其他外部代码依赖的情况下演示了同样的问题。我知道在这种特殊情况下,将两个截然不同的测试捆绑在一起并不是一个好的做法,但我认为它仍然说明了问题。

import spock.lang.*

@Unroll
class ExampleSpec extends Specification {

    def "minimum of #a and #b is #c and maximum of #a and #b is #d"() {
        expect:
        Math.min(a, b) == c
        Math.max(a, b) == d

        where:
        a | b || c | d
        3 | 7 || 3 | 7
        5 | 4 || 5 | 4  // <--- both c and d fail here
        9 | 9 || 9 | 9
    }
}

我认为这里有两种不同的东西在起作用。

  1. 在您的代码中出现失败的 assert 将引发错误,这将停止执行代码。这就是为什么在一个测试中不能有两个失败的断言。 Spock 中 expectthen 块中的任何代码行前面都有一个隐含的 assert
  2. 您在同一个测试中混合了正面和负面单元测试。我先 运行 进入这个,我 read/watched 关于这个和 Spock(我相信来自创建者 Peter Niederwieser)的一些事情,并且了解到这些应该分成不同的测试。不幸的是我找不到那个参考。所以基本上,您需要一项针对失败用例的测试,一项针对 passing/successful 个用例的测试。

根据该信息,这是您的第二个示例代码,测试被分开,失败场景在第二个测试中。

    @Unroll
    class ExampleSpec extends Specification {

        def "minimum of #a and #b is #c and maximum of #a and #b is #d - successes"() {
            expect:
            Math.min(a, b) == c
            Math.max(a, b) == d

            where:
            a | b || c | d
            3 | 7 || 3 | 7
            9 | 9 || 9 | 9
        }

        def "minimum of #a and #b is #c and maximum of #a and #b is #d - failures"() {
            expect:
            Math.min(a, b) != c
            Math.max(a, b) != d

            where:
            a | b || c | d
            5 | 4 || 5 | 4
        }
    }

至于你对 MongoDB 测试用例的评论 - 我不确定那里的意图是什么,但我猜他们正在做出几个全部通过的断言,而不是验证它们出问题了。

根据 OP 的 ,看起来与我之前的回答不同的解决方案会有所帮助。我将保留之前的答案,因为我觉得它仍然提供了与问题相关的有用信息(特别是区分正面和负面测试)。

鉴于您希望看到所有失败,而不仅仅是让它在第一个失败的断言处失败,我建议将所有内容连接到一个布尔 AND 运算中。不使用 && 快捷运算符,因为它只会 运行 直到第一次检查不满足整个操作。我建议使用 &,以便进行所有检查,而不管之前是否有任何失败的检查。

鉴于上面的 maxmin 示例,我会将 expect 块更改为:

Math.min(a, b) == c & Math.max(a, b) == d

发生故障时,它会为您提供以下信息:

Math.min(a, b) == c & Math.max(a, b) == d
     |   |  |  |  | |      |   |  |  |  |
     4   5  4  |  5 false  5   5  4  |  4
               false                 false

这会向您显示失败断言的每个部分。相比之下,如果您使用 &&,它只会显示第一次失败,如下所示:

Math.min(a, b) == c && Math.max(a, b) == d
     |   |  |  |  | |
     4   5  4  |  5 false
               false

如果您在一行中进行两次以上的检查,这显然会很快变得混乱 - 但这是您可以在一行中的所有失败信息与必须重新 运行 之间做出的权衡修复每个单独的组件后的测试。

希望对您有所帮助!