Groovy中的隐式断言语句

Groovy中的隐式断言语句,groovy,spock,Groovy,Spock,假设我有用Groovy编写的JUnit测试: class AssertTests { @Test void "explicit assert statement"() { def value = 42 assert value == 100 } @Test void "no assert statement"() { def value = 42 value == 100 } }

假设我有用Groovy编写的JUnit测试:

class AssertTests {

    @Test
    void "explicit assert statement"() {
        def value = 42
        assert value == 100
    }

    @Test
    void "no assert statement"() {
        def value = 42
        value == 100
    }

}
当我执行它们时, 由于assert语句,显式assert语句测试按预期失败

assert语句测试没有通过,我希望它会以与我使用assert语句时类似的方式失败


如何为用普通Groovy编写的测试实现隐式断言行为?

答案很简单-您不能。Spock使用AST转换来获取在then:部分中编写的代码,并将其转换为使断言等效的代码,而不是确切的断言

为了说明这一点,以下是您用Spock编写的测试:

导入spock.lang.Specification 类TestSpec扩展了规范{ def应该失败{ 什么时候: def值=42 然后: 断言值==100 } } 下面是它反编译回Java的字节码的样子:

// //IntelliJ IDEA从.class文件重新创建的源代码 //由Fernflower反编译器提供动力 // 导入groovy.lang.GroovyObject; 导入org.codehaus.groovy.runtime.ScriptBytecodeAdapter; 导入org.codehaus.groovy.runtime.callsite.callsite; 导入org.spockframework.runtime.ErrorCollector; 导入org.spockframework.runtime.SpockRuntime; 导入org.spockframework.runtime.ValueRecorder; 导入org.spockframework.runtime.model.BlockKind; 导入org.spockframework.runtime.model.BlockMetadata; 导入org.spockframework.runtime.model.FeatureMetadata; 导入org.spockframework.runtime.model.SpecMetadata; 导入spock.lang.Specification; @规范元数据 filename=TestSpec.groovy, 直线=5 公共类TestSpec扩展规范实现GroovyObject{ 公共测试规范{ CallSite[]var1=$getCallSiteArray; 超级的 } @特性元数据 行=7, name=应该失败, 序数=0, blocks={@BlockMetadata kind=BlockKind.WHEN, 文本={} ,@BlockMetadata 那么, 文本={} }, 参数名称={} 公共无效$spock\u功能\u 0\u 0{ CallSite[]var1=$getCallSiteArray; ErrorCollector$spock\u ErrorCollector=ErrorCollectorScriptBytecodeAdapter.castToTypevar1[0]。callConstructorErrorCollector.class,false,ErrorCollector.class; ValueRecorder$spock\u ValueRecorder=ValueRecorderScriptBytecodeAdapter.castToTypevar1[1]。调用构造函数ValueRecorder.class,ValueRecorder.class; 对象VAR1000; 试一试{ 对象值=42; 试一试{ SpockRuntime.verifyCondition$spock\u errorCollector,$spock\u valueRecorder.reset,value==100,Integer.ValueO12,Integer.ValueO16,Objectnull,$spock\u valueRecorder.record$spock\u valueRecorder.startRecordingValueInteger.ValueO2,ScriptBytecodeAdapter.compareEqual$spock\u valueRecorder.StartRecordingValueOf 0,value,$spock\u valueRecorder.record$spock\u valueRecorder.startRecordingValueInteger.valueof1100; var10000=null; }捕获可丢弃的var14{ SpockRuntime.ConditionFailed异常$spock\u errorCollector,$spock\u valueRecorder,value==100,Integer.valueO12,Integer.valueO16,Objectnull,var14; var10000=null; }最后{ ; } var1[2]。callvar1[3]。callthis.getSpecificationContext; }最后{ $spock_error collector.validateCollectedErrors; var10000=null; } } } 如果查看SpockRuntime类,您会发现verifyCondition方法会检查then:or and:块中找到的条件是否计算为true:

公共静态空间verifyCondition@NullableErrorCollector ErrorCollector,@Nullable ValueRecorder记录器, @可为空的字符串文本、整数行、整数列、@Nullable对象消息、@Nullable对象条件{ if!GroovyRuntimeUtil.isTruthycondition{ 最终条件NotSatisfiederRor条件NotSatisfiederRor=新条件NotSatisfiederRor 新条件GetValuesRecorder,text,TextPosition.createline,column,MessageToString消息,null,null; errorCollector.CollectorRowConditionNotSatisfiederError; } } 在JUnit测试中不能避免显式断言。您可以将它们隐藏在某个助手方法中,但仍然需要调用它们。请记住,JUnit runner要求测试方法返回void,因此无法捕获测试方法的结果。用def替换void,您将看到JUnit不会运行您的测试


如果您想在Groovy中探索AST的世界,可以尝试编写自己的AST转换。也许有一种方法可以找到布尔表达式并在其前面插入assert,但我不能保证它会工作。如果您在Groovy JUnit测试中为隐式断言寻找现成的解决方案,那么就没有这样的解决方案。

答案很简单——您不能。Spock使用AST转换获取在then:p中编写的代码 艺术,并将其转换为代码,使断言的等价物不是确切的断言

为了说明这一点,以下是您用Spock编写的测试:

导入spock.lang.Specification 类TestSpec扩展了规范{ def应该失败{ 什么时候: def值=42 然后: 断言值==100 } } 下面是它反编译回Java的字节码的样子:

// //IntelliJ IDEA从.class文件重新创建的源代码 //由Fernflower反编译器提供动力 // 导入groovy.lang.GroovyObject; 导入org.codehaus.groovy.runtime.ScriptBytecodeAdapter; 导入org.codehaus.groovy.runtime.callsite.callsite; 导入org.spockframework.runtime.ErrorCollector; 导入org.spockframework.runtime.SpockRuntime; 导入org.spockframework.runtime.ValueRecorder; 导入org.spockframework.runtime.model.BlockKind; 导入org.spockframework.runtime.model.BlockMetadata; 导入org.spockframework.runtime.model.FeatureMetadata; 导入org.spockframework.runtime.model.SpecMetadata; 导入spock.lang.Specification; @规范元数据 filename=TestSpec.groovy, 直线=5 公共类TestSpec扩展规范实现GroovyObject{ 公共测试规范{ CallSite[]var1=$getCallSiteArray; 超级的 } @特性元数据 行=7, name=应该失败, 序数=0, blocks={@BlockMetadata kind=BlockKind.WHEN, 文本={} ,@BlockMetadata 那么, 文本={} }, 参数名称={} 公共无效$spock\u功能\u 0\u 0{ CallSite[]var1=$getCallSiteArray; ErrorCollector$spock\u ErrorCollector=ErrorCollectorScriptBytecodeAdapter.castToTypevar1[0]。callConstructorErrorCollector.class,false,ErrorCollector.class; ValueRecorder$spock\u ValueRecorder=ValueRecorderScriptBytecodeAdapter.castToTypevar1[1]。调用构造函数ValueRecorder.class,ValueRecorder.class; 对象VAR1000; 试一试{ 对象值=42; 试一试{ SpockRuntime.verifyCondition$spock\u errorCollector,$spock\u valueRecorder.reset,value==100,Integer.ValueO12,Integer.ValueO16,Objectnull,$spock\u valueRecorder.record$spock\u valueRecorder.startRecordingValueInteger.ValueO2,ScriptBytecodeAdapter.compareEqual$spock\u valueRecorder.StartRecordingValueOf 0,value,$spock\u valueRecorder.record$spock\u valueRecorder.startRecordingValueInteger.valueof1100; var10000=null; }捕获可丢弃的var14{ SpockRuntime.ConditionFailed异常$spock\u errorCollector,$spock\u valueRecorder,value==100,Integer.valueO12,Integer.valueO16,Objectnull,var14; var10000=null; }最后{ ; } var1[2]。callvar1[3]。callthis.getSpecificationContext; }最后{ $spock_error collector.validateCollectedErrors; var10000=null; } } } 如果查看SpockRuntime类,您会发现verifyCondition方法会检查then:or and:块中找到的条件是否计算为true:

公共静态空间verifyCondition@NullableErrorCollector ErrorCollector,@Nullable ValueRecorder记录器, @可为空的字符串文本、整数行、整数列、@Nullable对象消息、@Nullable对象条件{ if!GroovyRuntimeUtil.isTruthycondition{ 最终条件NotSatisfiederRor条件NotSatisfiederRor=新条件NotSatisfiederRor 新条件GetValuesRecorder,text,TextPosition.createline,column,MessageToString消息,null,null; errorCollector.CollectorRowConditionNotSatisfiederError; } } 在JUnit测试中不能避免显式断言。您可以将它们隐藏在某个助手方法中,但仍然需要调用它们。请记住,JUnit runner要求测试方法返回void,因此无法捕获测试方法的结果。用def替换void,您将看到JUnit不会运行您的测试


如果您想在Groovy中探索AST的世界,可以尝试编写自己的AST转换。也许有一种方法可以找到布尔表达式并在其前面插入assert,但我不能保证它会工作。如果您想为Groovy JUnit测试中的隐式断言寻找一个现成的解决方案,那么就没有这样的解决方案。

非常感谢您的回答和元编程技巧。我在JUnit runner中也遇到了void vs def问题。出于好奇,我在哪里可以找到这个JUnit运行程序对测试方法必须返回void的期望呢?@PatrikMihalčin,JUnit4在向非void方法添加@test注释时失败,因为这部分-。JUnit5只是忽略了用@Test注释的非void方法——我在@PatrikMihalčin的这篇博文中解释了为什么会发生这种情况,附带说明:如果您喜欢在测试中舒适地使用Spock测试DSL,是什么阻止您使用Spock而不是JUnit或任何其他类型的DSL
您现在使用的f测试?您的工作环境中是否存在阻力或技术限制?@kriegaex我最近接管了一个基于JUnit的测试项目。。迟早我会介绍斯波克的。。我的好奇心驱使我打开了这个问题:非常感谢你的回答和元编程技巧。我在JUnit runner中也遇到了void vs def问题。出于好奇,我在哪里可以找到这个JUnit运行程序对测试方法必须返回void的期望呢?@PatrikMihalčin,JUnit4在向非void方法添加@test注释时失败,因为这部分-。JUnit5只是忽略了用@Test注释的非void方法——我在@PatrikMihalčin的这篇博文中解释了为什么会发生这种情况,附带说明:如果您喜欢在测试中使用Spock测试DSL,那么是什么阻止您使用Spock而不是JUnit或您现在使用的任何其他类型的测试?您的工作环境中是否存在阻力或技术限制?@kriegaex我最近接管了一个基于JUnit的测试项目。。迟早我会介绍斯波克的。。我的好奇心驱使我打开了这个问题: