【问题标题】:Conditional condition not working as expected条件条件未按预期工作
【发布时间】:2021-12-13 11:59:12
【问题描述】:

我注意到then 块中的条件如何被评估的奇怪行为。条件应该在执行when 块之后进行评估,但在一种特定情况下,它会在之前执行,因此失败。我可以使用以下简单规范重现该行为:

import spock.lang.Specification

class ConditionalCondition extends Specification {
    def 'non-working condition check'() {
        given:
            def result = 0

        when:
            System.out.println("----- before: ${result}")
            result = 1
            System.out.println("----- after: ${result}")

        then:
            if (true) {
                assert result == 1
            }
            else {
                [Mock(Closure)].each {
                    it.call() >> ""
                }
            }
    }

    def 'working condition check'() {
        given:
            def result = 0

        when:
            System.out.println("----- before: ${result}")
            result = 1
            System.out.println("----- after: ${result}")

        then:
            if (true) {
                assert result == 1
            }
            if (false) {
                [Mock(Closure)].each {
                    it.call() >> ""
                }
            }
    }
}

如果您运行上述程序,您会看到如下所示的输出:

ConditionalCondition > non-working condition check FAILED
    org.spockframework.runtime.SpockComparisonFailure at ConditionalCondition.groovy:15

ConditionalCondition > working condition check STANDARD_OUT
    ----- before: 0
    ----- after: 1

如您所见,即使是控制台输出也不会显示,因为 then 块中的代码永远不会执行。报告看起来像这样,再次证实了同样的事实:

  non-working condition check

 Condition not satisfied:

 result == 1
 |      |
 0      false

         at ConditionalCondition.non-working condition check(ConditionalCondition.groovy:15)

Tests

   Test                        Duration Result
   non-working condition check 0.350s   failed
   working condition check     0.030s   passed

这两个测试之间的唯一区别是以下代码周围的条件:

                [Mock(Closure)].each {
                    it.call() >> ""
                }

代码本身对测试没有任何用处,但是当代码是 else 块时它不起作用,但如果它处于单独的 if 条件下它工作正常。此外,如果我将代码移出块或完全删除代码,测试将按预期工作。

谁能帮我理解这种奇怪的行为?

【问题讨论】:

    标签: groovy spock


    【解决方案1】:

    为此,您必须深入了解 Spock 的工作原理,这很神奇。

    制作类似的东西

    def "mock example"() {
            given:
            Receiver receiver = Mock()
            def producer = new Producer(receiver)
            
            when:
            producer.createItem()
        
            then:
            1 * receiver.receive(_) >> true
        }
    

    工作,它将所有模拟交互声明从 then 块直接移到 when 块代码前面。

    这就是代码在 AST 转换后的样子。

    @org.spockframework.runtime.model.FeatureMetadata(name = 'mock example', ordinal = 2, line = 43, blocks = [org.spockframework.runtime.model.BlockKind.SETUP[]org.codehaus.groovy.ast.AnnotationNode@d9f41, org.spockframework.runtime.model.BlockKind.WHEN[]org.codehaus.groovy.ast.AnnotationNode@d9f41, org.spockframework.runtime.model.BlockKind.THEN[]org.codehaus.groovy.ast.AnnotationNode@d9f41], parameterNames = [])
        public void $spock_feature_0_2() {
            Receiver receiver = this.MockImpl('receiver', Receiver)
            java.lang.Object producer = new Producer(receiver)
            this.getSpecificationContext().getMockController().enterScope()
            this.getSpecificationContext().getMockController().addInteraction(new org.spockframework.mock.runtime.InteractionBuilder(52, 9, '1 * receiver.receive(_) >> true').setFixedCount(1).addEqualTarget(receiver).addEqualMethodName('receive').setArgListKind(true, false).addEqualArg(_).addConstantResponse(true).build())
            producer.createItem()
            this.getSpecificationContext().getMockController().leaveScope()
            this.getSpecificationContext().getMockController().leaveScope()
        }
    

    如果我们获取您的代码,那么我们可以看到同样的事情发生了。

        @org.spockframework.runtime.model.FeatureMetadata(name = 'non-working condition check', ordinal = 0, line = 5, blocks = [org.spockframework.runtime.model.BlockKind.SETUP[]org.codehaus.groovy.ast.AnnotationNode@d9f41, org.spockframework.runtime.model.BlockKind.WHEN[]org.codehaus.groovy.ast.AnnotationNode@d9f41, org.spockframework.runtime.model.BlockKind.THEN[]org.codehaus.groovy.ast.AnnotationNode@d9f41], parameterNames = [])
        public void $spock_feature_0_0() {
            org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
            org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
            java.lang.Object result = 0
            this.getSpecificationContext().getMockController().enterScope()
            then:
            if (true) {
                try {
                    org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'result == 1', 16, 24, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), result) == $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), 1)))
                } 
                catch (java.lang.Throwable $spock_condition_throwable) {
                    org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'result == 1', 16, 24, null, $spock_condition_throwable)} 
                finally { 
                } 
            } else {
                [this.MockImpl(null, null, groovy.lang.Closure)].each({ 
                    return this.getSpecificationContext().getMockController().addInteraction(new org.spockframework.mock.runtime.InteractionBuilder(20, 21, 'it.call() >> ""').addEqualTarget(it).addEqualMethodName('call').setArgListKind(true, false).addConstantResponse('').build())
                })
            }
            java.lang.System.out.println("----- before: $result")
            result = 1
            java.lang.System.out.println("----- after: $result")
            this.getSpecificationContext().getMockController().leaveScope()
            this.getSpecificationContext().getMockController().leaveScope()
        }
    
        @org.spockframework.runtime.model.FeatureMetadata(name = 'working condition check', ordinal = 1, line = 25, blocks = [org.spockframework.runtime.model.BlockKind.SETUP[]org.codehaus.groovy.ast.AnnotationNode@d9f41, org.spockframework.runtime.model.BlockKind.WHEN[]org.codehaus.groovy.ast.AnnotationNode@d9f41, org.spockframework.runtime.model.BlockKind.THEN[]org.codehaus.groovy.ast.AnnotationNode@d9f41], parameterNames = [])
        public void $spock_feature_0_1() {
            org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
            org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
            java.lang.Object result = 0
            java.lang.System.out.println("----- before: $result")
            result = 1
            java.lang.System.out.println("----- after: $result")
            then:
            if (true) {
                try {
                    org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'result == 1', 36, 24, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), result) == $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), 1)))
                } 
                catch (java.lang.Throwable $spock_condition_throwable) {
                    org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'result == 1', 36, 24, null, $spock_condition_throwable)} 
                finally { 
                } 
            }
            this.getSpecificationContext().getMockController().leaveScope()
        }
    
    

    【讨论】:

    • 在您的示例中,addInteraction 不会立即对其进行评估,对吗?让我尝试对我的示例代码进行相同的分析,看看会发生什么。
    • 看了两个反编译的方法,明白了你解释的上下文是怎么回事。 if/else 失败的原因是 Spock 将整个块移动到 when 块之前,因为它不能只移动 else 块。当交互处于单独的 if 条件时,只有该块被移动,因此它可以工作。感谢您帮助我理解这一点,我原本以为是 Spock 错误。
    • 您偶然发现了一种罕见的极端情况,即 Spock 魔法无法实现您想要的效果,如果不深入了解它的工作原理,就很难理解为什么。
    猜你喜欢
    • 2015-04-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-23
    相关资源
    最近更新 更多