Usage of final
in a feature method with cleanup
#1017
You might run into issues if you use final
in your Specification.
Groovy 2.5 introduced a final checker which broke previously compiling code.
tl;dr: Do not use final
inside of features if you get compile errors.
Users combining
final
and@CompileStatic
orfinal
and Spock may see errors from thefinal
variable analyzer. Work is underway to resolve those error messages. You may need to temporarily remove the final modifier in the meantime.
class Broken extends Specification {
def "test"() {
final value = 'hello'
expect:
value.size() > 3
cleanup:
clean value
}
def clean(v) {}
}
Will fail with something like.
> Task :compileTestGroovy FAILED startup failed: /Users/acme/projects/spock-tests/src/test/groovy/Broken.groovy: 11: The variable [value] may be uninitialized . At [11:15] @ line 11, column 15. clean value
This is due to how Spock implements cleanup
, by wrapping the whole body in a try-finally
block during the AST transformation.
If you look at the compiled code you can see how it was transformed
try {
java.lang.Object value // (1)
java.lang.Throwable $spock_feature_throwable
try {
value = 'hello' // (2)
try {
org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'value.size() > 3', 8, 9, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(5), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(3), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), value).$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), 'size')()) > $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(4), 3)))
}
catch (java.lang.Throwable throwable) {
org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'value.size() > 3', 8, 9, null, throwable)}
finally {
}
}
catch (java.lang.Throwable $spock_tmp_throwable) {
$spock_feature_throwable = $spock_tmp_throwable
throw $spock_tmp_throwable
}
finally {
try {
this.clean(value) // (3)
}
catch (java.lang.Throwable $spock_tmp_throwable) {
if ( $spock_feature_throwable != null) {
$spock_feature_throwable.addSuppressed($spock_tmp_throwable)
} else {
throw $spock_tmp_throwable
}
}
finally {
}
}
this.getSpecificationContext().getMockController().leaveScope()
}
finally {
$spock_errorCollector.validateCollectedErrors()
}
-
Here it moved the variable declaration outside of the
try-finally
block -
Here it tries to initialize the field
-
Here it is passed to cleanup
The catch-22 is that the variable must be declared outside of the try-finally
block, to be available in the finally,
but for the variable initialization to be covered by the cleanup it must be initialized inside the try
block.
This works fine for normal variables, but final variable can only be initialized when they are declared.
Using Traits with Specifications
Traits on Specifications are not supported by Spock, some use-cases might work while others don’t. This is due to how groovy implements traits and AST transformations.
Traits are not officially compatible with AST transformations. Some of them, like @CompileStatic will be applied on the trait itself (not on implementing classes), while others will apply on both the implementing class and the trait. There is absolutely no guarantee that an AST transformation will run on a trait as it does on a regular class, so use it at your own risk!
Groovy version compatibility
By default, Spock can only be used with the Groovy version it was compiled with. It means that Spock 2.0-groovy-2.5
can only executed with Groovy 2.5.x, 2.0-groovy-3.0
with 3.0.x, etc. That restriction was introduced to help users find an appropriate Groovy variant and limit number of reported invalid issues caused by the incompatibilities between the major Groovy versions.
However, occasionally it might be useful to be able to play with the next (officially unsupported) Groovy version, especially that usually, in the majority of cases, it should just work fine. Starting with Groovy 2.0 that restriction has been relaxed in the Spock SNAPSHOT versions. In addition, the early adopters can implicitly disable that check - also in the production versions - providing the system property -Dspock.iKnowWhatImDoing.disableGroovyVersionCheck=true
. Please bear in mind, however, that it is completely unsupported and might lead to some unexpected errors.