据我所知,验证测试结果的一种方法是将表达式写入then节,其结果为布尔值。
但是最近我经历了一种我不了解的行为。似乎当人们尝试验证某项内容时,即在一个块中, then 断言仅适用于明确的 assert 关键字。
这里是示例。我编写了一个虚拟if语句来包含一个块,但是对于for循环或任何控制流来说都是一样的。
def "test fails as expected"() {
when: "result has some value"
def result = "someValue"
then: "result has the expected value"
result == "otherValue"
}
def "test passes, but shouldn't"() {
when: "result has some value"
def result = "someValue"
then: "result has the expected value"
if (true) {
result == "otherValue"
}
}
def "test fails as expected when using assert"() {
when: "result has some value"
def result = "someValue"
then: "result has the expected value"
if (true) {
assert result == "otherValue"
}
}
我发现这种行为有点误导。有人可以解释为什么会这样吗?这是错误还是用法不正确?
答案 0 :(得分:3)
InputStream inputStream = response.getRawBody(); BufferedInputStream bis = new BufferedInputStream(inputStream); ByteArrayOutputStream buf = new ByteArrayOutputStream(); int result = bis.read(); while(result != -1) { buf.write((byte) result); result = bis.read(); } String rawJson = buf.toString("UTF-8");
和when
块始终一起出现。他们描述了刺激和预期的反应。then
块可以包含任意代码,而when
块仅限于条件,例外条件,交互和变量定义。。特征方法可能包含多对then
块。
这说明了为什么Spocks AST变压器看不到以下when-then
块:
then
是正确的,它不会将其转换为then:
if (true) {
result == "otherValue"
}
调用。
如果编译您的类(启用静态编译以提高可读性)并检查字节码,您将看到类似以下内容:
SpockRuntime.verifyCondition()
现在,如果我们分析这段代码,我们将发现以下Spock测试用例:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
import groovy.lang.GroovyObject;
import org.codehaus.groovy.runtime.DefaultGroovyMethods;
import org.codehaus.groovy.runtime.ScriptBytecodeAdapter;
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 = "OtherSpec.groovy",
line = 4
)
public class OtherSpec extends Specification implements GroovyObject {
public OtherSpec() {
}
public Object test(String result) {
return true ? ScriptBytecodeAdapter.compareEqual(result, "otherValue") : null;
}
@FeatureMetadata(
line = 7,
name = "test fails as expected",
ordinal = 0,
blocks = {@BlockMetadata(
kind = BlockKind.WHEN,
texts = {"result has some value"}
), @BlockMetadata(
kind = BlockKind.THEN,
texts = {"result has the expected value"}
)},
parameterNames = {}
)
public void $spock_feature_0_0() {
ErrorCollector $spock_errorCollector = new ErrorCollector(false);
ValueRecorder $spock_valueRecorder = new ValueRecorder();
Object var10000;
try {
String result = "someValue";
try {
SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), "result == \"otherValue\"", Integer.valueOf(12), Integer.valueOf(9), (Object)null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(Integer.valueOf(2)), ScriptBytecodeAdapter.compareEqual($spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(Integer.valueOf(0)), result), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(Integer.valueOf(1)), "otherValue"))));
var10000 = null;
} catch (Throwable var13) {
SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, "result == \"otherValue\"", Integer.valueOf(12), Integer.valueOf(9), (Object)null, var13);
var10000 = null;
} finally {
;
}
ScriptBytecodeAdapter.invokeMethod0(OtherSpec.class, ((OtherSpec)this).getSpecificationContext().getMockController(), (String)"leaveScope");
} finally {
$spock_errorCollector.validateCollectedErrors();
var10000 = null;
}
}
@FeatureMetadata(
line = 15,
name = "test passes, but shouldn't",
ordinal = 1,
blocks = {@BlockMetadata(
kind = BlockKind.WHEN,
texts = {"result has some value"}
), @BlockMetadata(
kind = BlockKind.THEN,
texts = {"result has the expected value"}
)},
parameterNames = {}
)
public void $spock_feature_0_1() {
String result = "someValue";
if (true) {
ScriptBytecodeAdapter.compareEqual(result, "otherValue");
}
ScriptBytecodeAdapter.invokeMethod0(OtherSpec.class, ((OtherSpec)this).getSpecificationContext().getMockController(), (String)"leaveScope");
}
@FeatureMetadata(
line = 25,
name = "test fails as expected when using assert",
ordinal = 2,
blocks = {@BlockMetadata(
kind = BlockKind.WHEN,
texts = {"result has some value"}
), @BlockMetadata(
kind = BlockKind.THEN,
texts = {"result has the expected value"}
)},
parameterNames = {}
)
public void $spock_feature_0_2() {
ErrorCollector $spock_errorCollector = new ErrorCollector(false);
ValueRecorder $spock_valueRecorder = new ValueRecorder();
Object var10000;
try {
String result = "someValue";
DefaultGroovyMethods.println(this, this.test("otherValue"));
var10000 = null;
if (true) {
try {
SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), "result == \"otherValue\"", Integer.valueOf(32), Integer.valueOf(20), (Object)null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(Integer.valueOf(2)), ScriptBytecodeAdapter.compareEqual($spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(Integer.valueOf(0)), result), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(Integer.valueOf(1)), "otherValue"))));
var10000 = null;
} catch (Throwable var13) {
SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, "result == \"otherValue\"", Integer.valueOf(32), Integer.valueOf(20), (Object)null, var13);
var10000 = null;
} finally {
;
}
}
ScriptBytecodeAdapter.invokeMethod0(OtherSpec.class, ((OtherSpec)this).getSpecificationContext().getMockController(), (String)"leaveScope");
} finally {
$spock_errorCollector.validateCollectedErrors();
var10000 = null;
}
}
}
编译成这样:
def "test fails as expected"() {
when: "result has some value"
def result = "someValue"
then: "result has the expected value"
result == "otherValue"
}
还有将断言放在if语句中的测试用例:
public void $spock_feature_0_0() {
ErrorCollector $spock_errorCollector = new ErrorCollector(false);
ValueRecorder $spock_valueRecorder = new ValueRecorder();
Object var10000;
try {
String result = "someValue";
try {
SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), "result == \"otherValue\"", Integer.valueOf(12), Integer.valueOf(9), (Object)null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(Integer.valueOf(2)), ScriptBytecodeAdapter.compareEqual($spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(Integer.valueOf(0)), result), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(Integer.valueOf(1)), "otherValue"))));
var10000 = null;
} catch (Throwable var13) {
SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, "result == \"otherValue\"", Integer.valueOf(12), Integer.valueOf(9), (Object)null, var13);
var10000 = null;
} finally {
;
}
ScriptBytecodeAdapter.invokeMethod0(OtherSpec.class, ((OtherSpec)this).getSpecificationContext().getMockController(), (String)"leaveScope");
} finally {
$spock_errorCollector.validateCollectedErrors();
var10000 = null;
}
}
编译成这样:
def "test passes, but shouldn't"() {
when: "result has some value"
def result = "someValue"
then: "result has the expected value"
if (true) {
result == "otherValue"
}
}
如果您有兴趣调查此AST转换的源代码,可以先进行以下分析:
在最后一个用例中,在if语句块中添加public void $spock_feature_0_1() {
String result = "someValue";
if (true) {
ScriptBytecodeAdapter.compareEqual(result, "otherValue");
}
ScriptBytecodeAdapter.invokeMethod0(OtherSpec.class, ((OtherSpec)this).getSpecificationContext().getMockController(), (String)"leaveScope");
}
是Spock的显式指令,必须将其转换为验证条件调用。这就是为什么您会看到将字节码反编译成这样的原因:
assert
请注意,public void $spock_feature_0_2() {
ErrorCollector $spock_errorCollector = new ErrorCollector(false);
ValueRecorder $spock_valueRecorder = new ValueRecorder();
Object var10000;
try {
String result = "someValue";
DefaultGroovyMethods.println(this, this.test("otherValue"));
var10000 = null;
if (true) {
try {
SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), "result == \"otherValue\"", Integer.valueOf(32), Integer.valueOf(20), (Object)null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(Integer.valueOf(2)), ScriptBytecodeAdapter.compareEqual($spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(Integer.valueOf(0)), result), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(Integer.valueOf(1)), "otherValue"))));
var10000 = null;
} catch (Throwable var13) {
SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, "result == \"otherValue\"", Integer.valueOf(32), Integer.valueOf(20), (Object)null, var13);
var10000 = null;
} finally {
;
}
}
ScriptBytecodeAdapter.invokeMethod0(OtherSpec.class, ((OtherSpec)this).getSpecificationContext().getMockController(), (String)"leaveScope");
} finally {
$spock_errorCollector.validateCollectedErrors();
var10000 = null;
}
}
仍然存在,因为AST转换器仍然忽略对其进行转换,但是条件是:
if (true) { /*...*/ }
被AST变压器访问并接受,并替换为:
assert result == "otherValue"