Groovy 中的隐式断言语句
Implicit assert statement in Groovy
假设我用 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
语句,explicit assert statement
测试按预期失败。
no assert statement
测试通过,我希望它会以与我使用 http://spockframework.org
时类似的方式失败
如何实现 implicit assert
以纯 Groovy 编写的测试的行为?
答案很简单 - 你不能。 Spock 使用 AST 转换来抓取 then:
部分中编写的代码,并将其转换为与断言等效的代码(不是确切的 assert
.)
为了说明这一点,这是您用 Spock 编写的测试:
import spock.lang.Specification
class TestSpec extends Specification {
def "should fail"() {
when:
def value = 42
then:
assert value == 100
}
}
这是它的字节码反编译回 Java 的样子:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
import groovy.lang.GroovyObject;
import org.codehaus.groovy.runtime.ScriptBytecodeAdapter;
import org.codehaus.groovy.runtime.callsite.CallSite;
import org.spockframework.runtime.ErrorCollector;
import org.spockframework.runtime.SpockRuntime;
import org.spockframework.runtime.ValueRecorder;
import org.spockframework.runtime.model.BlockKind;
import org.spockframework.runtime.model.BlockMetadata;
import org.spockframework.runtime.model.FeatureMetadata;
import org.spockframework.runtime.model.SpecMetadata;
import spock.lang.Specification;
@SpecMetadata(
filename = "TestSpec.groovy",
line = 5
)
public class TestSpec extends Specification implements GroovyObject {
public TestSpec() {
CallSite[] var1 = $getCallSiteArray();
super();
}
@FeatureMetadata(
line = 7,
name = "should fail",
ordinal = 0,
blocks = {@BlockMetadata(
kind = BlockKind.WHEN,
texts = {}
), @BlockMetadata(
kind = BlockKind.THEN,
texts = {}
)},
parameterNames = {}
)
public void $spock_feature_0_0() {
CallSite[] var1 = $getCallSiteArray();
ErrorCollector $spock_errorCollector = (ErrorCollector)ScriptBytecodeAdapter.castToType(var1[0].callConstructor(ErrorCollector.class, false), ErrorCollector.class);
ValueRecorder $spock_valueRecorder = (ValueRecorder)ScriptBytecodeAdapter.castToType(var1[1].callConstructor(ValueRecorder.class), ValueRecorder.class);
Object var10000;
try {
Object value = 42;
try {
SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), "value == 100", Integer.valueOf(12), Integer.valueOf(16), (Object)null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(Integer.valueOf(2)), ScriptBytecodeAdapter.compareEqual($spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(Integer.valueOf(0)), value), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(Integer.valueOf(1)), 100))));
var10000 = null;
} catch (Throwable var14) {
SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, "value == 100", Integer.valueOf(12), Integer.valueOf(16), (Object)null, var14);
var10000 = null;
} finally {
;
}
var1[2].call(var1[3].call(this.getSpecificationContext()));
} finally {
$spock_errorCollector.validateCollectedErrors();
var10000 = null;
}
}
}
如果你查看 SpockRuntime
class,你会发现 verifyCondition
方法检查是否在 then:
或 and:
块中找到条件计算结果为 true
:
public static void verifyCondition(@Nullable ErrorCollector errorCollector, @Nullable ValueRecorder recorder,
@Nullable String text, int line, int column, @Nullable Object message, @Nullable Object condition) {
if (!GroovyRuntimeUtil.isTruthy(condition)) {
final ConditionNotSatisfiedError conditionNotSatisfiedError = new ConditionNotSatisfiedError(
new Condition(getValues(recorder), text, TextPosition.create(line, column), messageToString(message), null, null));
errorCollector.collectOrThrow(conditionNotSatisfiedError);
}
}
您无法在 JUnit 测试中避免显式断言。您可以将它们隐藏在一些辅助方法中,但您仍然需要调用它们。请记住,JUnit 运行ner 需要测试方法 returns void
,因此您无法捕获测试方法的结果。 (将 void
替换为 def
,您会发现 JUnit 不会 运行 您的测试。)
如果您想在 Groovy 中探索 compile-time metaprogramming 的世界,您可以尝试编写自己的 AST 转换。也许有一种方法可以找到布尔表达式并在它前面注入 assert
,但我不能保证它会起作用。如果您在 Groovy JUnit 测试中寻找隐式断言的现成解决方案,那么没有这样的解决方案。
假设我用 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
语句,explicit assert statement
测试按预期失败。
no assert statement
测试通过,我希望它会以与我使用 http://spockframework.org
如何实现 implicit assert
以纯 Groovy 编写的测试的行为?
答案很简单 - 你不能。 Spock 使用 AST 转换来抓取 then:
部分中编写的代码,并将其转换为与断言等效的代码(不是确切的 assert
.)
为了说明这一点,这是您用 Spock 编写的测试:
import spock.lang.Specification
class TestSpec extends Specification {
def "should fail"() {
when:
def value = 42
then:
assert value == 100
}
}
这是它的字节码反编译回 Java 的样子:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
import groovy.lang.GroovyObject;
import org.codehaus.groovy.runtime.ScriptBytecodeAdapter;
import org.codehaus.groovy.runtime.callsite.CallSite;
import org.spockframework.runtime.ErrorCollector;
import org.spockframework.runtime.SpockRuntime;
import org.spockframework.runtime.ValueRecorder;
import org.spockframework.runtime.model.BlockKind;
import org.spockframework.runtime.model.BlockMetadata;
import org.spockframework.runtime.model.FeatureMetadata;
import org.spockframework.runtime.model.SpecMetadata;
import spock.lang.Specification;
@SpecMetadata(
filename = "TestSpec.groovy",
line = 5
)
public class TestSpec extends Specification implements GroovyObject {
public TestSpec() {
CallSite[] var1 = $getCallSiteArray();
super();
}
@FeatureMetadata(
line = 7,
name = "should fail",
ordinal = 0,
blocks = {@BlockMetadata(
kind = BlockKind.WHEN,
texts = {}
), @BlockMetadata(
kind = BlockKind.THEN,
texts = {}
)},
parameterNames = {}
)
public void $spock_feature_0_0() {
CallSite[] var1 = $getCallSiteArray();
ErrorCollector $spock_errorCollector = (ErrorCollector)ScriptBytecodeAdapter.castToType(var1[0].callConstructor(ErrorCollector.class, false), ErrorCollector.class);
ValueRecorder $spock_valueRecorder = (ValueRecorder)ScriptBytecodeAdapter.castToType(var1[1].callConstructor(ValueRecorder.class), ValueRecorder.class);
Object var10000;
try {
Object value = 42;
try {
SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), "value == 100", Integer.valueOf(12), Integer.valueOf(16), (Object)null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(Integer.valueOf(2)), ScriptBytecodeAdapter.compareEqual($spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(Integer.valueOf(0)), value), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(Integer.valueOf(1)), 100))));
var10000 = null;
} catch (Throwable var14) {
SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, "value == 100", Integer.valueOf(12), Integer.valueOf(16), (Object)null, var14);
var10000 = null;
} finally {
;
}
var1[2].call(var1[3].call(this.getSpecificationContext()));
} finally {
$spock_errorCollector.validateCollectedErrors();
var10000 = null;
}
}
}
如果你查看 SpockRuntime
class,你会发现 verifyCondition
方法检查是否在 then:
或 and:
块中找到条件计算结果为 true
:
public static void verifyCondition(@Nullable ErrorCollector errorCollector, @Nullable ValueRecorder recorder,
@Nullable String text, int line, int column, @Nullable Object message, @Nullable Object condition) {
if (!GroovyRuntimeUtil.isTruthy(condition)) {
final ConditionNotSatisfiedError conditionNotSatisfiedError = new ConditionNotSatisfiedError(
new Condition(getValues(recorder), text, TextPosition.create(line, column), messageToString(message), null, null));
errorCollector.collectOrThrow(conditionNotSatisfiedError);
}
}
您无法在 JUnit 测试中避免显式断言。您可以将它们隐藏在一些辅助方法中,但您仍然需要调用它们。请记住,JUnit 运行ner 需要测试方法 returns void
,因此您无法捕获测试方法的结果。 (将 void
替换为 def
,您会发现 JUnit 不会 运行 您的测试。)
如果您想在 Groovy 中探索 compile-time metaprogramming 的世界,您可以尝试编写自己的 AST 转换。也许有一种方法可以找到布尔表达式并在它前面注入 assert
,但我不能保证它会起作用。如果您在 Groovy JUnit 测试中寻找隐式断言的现成解决方案,那么没有这样的解决方案。