【问题标题】:Executing all assertions in the same Spock test, even if one of them fails在同一个 Spock 测试中执行所有断言,即使其中一个失败
【发布时间】:2015-11-04 18:53:13
【问题描述】:

我试图在运行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
    }
}

【问题讨论】:

  • 在您提供的 mongo 测试中,它也会依次失败。
  • 两个 cmets:1) 您是否能够创建一个测试来演示此问题而无需任何其他外部代码依赖项? 2) 为什么你的where 块中有一个双管 (||)?这不是我认为应该使用的格式。
  • @mnd,见here|| 完全有效。
  • 您希望它在测试中运行所有断言,即使第一个断言失败?
  • @Opal 你说得对,谢谢你告诉我。

标签: testing groovy automated-tests spock


【解决方案1】:

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

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

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

Math.min(a, b) == c &amp; 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

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

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

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

希望这会有所帮助!

【讨论】:

  • 使用&amp; 运算符是个好主意,我没有想到,谢谢!测试报告确实变得有些混乱,但我想它仍然没问题,因为似乎没有其他选择。
  • 注意:我重新编辑了我的问题(包括更改其标题)以进一步澄清它,并希望使它更有用,因为您发现我之前的评论很有帮助。
【解决方案2】:

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

  1. 在您的代码中包含失败的assert 将引发错误,这将停止执行代码。这就是为什么在一个测试中不能有两个失败的断言。 Spock 中 expectthen 块中的任何代码行前面都有一个隐含的 assert
  2. 您在同一个测试中混合了正面和负面的单元测试。我在自己之前遇到了这个问题,我阅读/观看了一些关于这个和 Spock 的内容(我相信来自创作者 Peter Niederwieser),并了解到这些应该分成不同的测试。不幸的是,我找不到那个参考。因此,基本上,您需要一项针对失败用例的测试,以及一项针对通过/成功用例的测试。

鉴于该信息,这是您的第二个示例代码,其中测试分开,第二个测试中出现失败的场景。

    @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 测试用例的评论 - 我不确定那里的意图是什么,但我猜他们正在做出几个都通过的断言,而不是验证某些事情失败了。

【讨论】:

  • 感谢您的回答。关于你的第二点:我想我可能被误解了。请参阅上面我重新编辑的问题。 (顺便说一句,我的另一个带有 min 和 max 的示例可能不能很好地代表手头的问题。)问题是我事先不知道哪些情况会失败:例如,传递给 reportedErrorMessagereportedErrorCode 的内容可能是垃圾值(与预期值不同),我想了解这些值。另外,我不确定您为什么在第一种方法中包含 assert false 设置。
  • 我看到了你的更新。对于您使用name=''password=null 给出的示例,您是否有不想设置expectedErrorMessage=Empty!expectedErrorCode=10001 的原因?另外,我清理了我的答案以删除assert false,不知道为什么我把它放在那里,谢谢。
  • 上面的代码只是一个例子,设计要求是如果namepassword' 'null,那么我应该总是期望完全相同的expectedErrorMessage = 'Empty credentials!'expectedErrorCode = 10003。如果由于某种原因(可能是由于源代码中的错误)我得到expectedErrorMessage = Empty!(或'Empty credentials!'以外的任何其他内容)和expectedErrorCode = 10001(或1003以外的任何其他内容),这将不满足上述要求要求。我希望能够检测到所有这些失败的案例。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-10-31
  • 2013-05-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多