Hello,
I'm facing the issue that variable binding in where block are evaluated before hooks before / setup which may override static variable that is being used in where block as method argument.
Is it expected to be like this or is it a bug?
class TestClassShould extends Specification {
private static String text // null by default
@Before
void before() {
println "called before"
text = "some text entered"
}
void setup() {
println "called setup"
text = "some text entered"
}
def "should evaluate variables in where block after before hook"() {
expect:
println "ACTUAL value: $actual"
actual != null
where:
actual | _
foo(text) | _ // evaluation of "message" variable happens before "before" / "setup" hooks
}
String foo(String str){
println "evaluating foo($str)"
str
}
This test fails because it's not calling before or setup hooks before evaluating variables from where block.
Test output is following:
evaluating foo(null)
called before
called setup
ACTUAL value: null
Condition not satisfied:
actual != null
| |
null false
------------------------------------------------------------
Gradle 5.6.4
------------------------------------------------------------
Groovy: 2.5.4
Ant: Apache Ant(TM) version 1.9.14 compiled on March 12 2019
JVM: 11.0.8 (AdoptOpenJDK 11.0.8+10)
OS: Windows 10 10.0 amd64
gradle: compile 'org.spockframework:spock-core:1.3-groovy-2.5'
We're using Spock with JUnit 4.
That's actually expected.
The where block is extracted and evaluated before the tests run.
You also don't know how many iterations you would have.
Maybe you didn't notice, but if you duplicate your last line in the where block, even both foo calls are made before the first setup method.
The data table syntax you use is semantically identical to actual << [foo(text)] or in your example even actual = foo(text).
If you would use some lazy iterable as data provider instead of a simple list (for which the table syntax is just syntactic sugar),
that only evaluates the method when the next value is requested, the methods would not be executed all before the first setup invocation, but still each method would be invoked before the setup method for its iteration, because first the data provider has to provide the next value to see that there is actually another iteration to execute.
To get what you want you could for example delay the method invocation by wrapping it in a closure that you then call within the feature method invocation, for example like this:
def "should evaluate variables in where block after before hook"() {
given:
def actual = actualClosure()
expect:
println "ACTUAL value: $actual"
actual != null
where:
_ | actualClosure
_ | { foo(text) }
}
Two more things:
@Before is a JUnit4 annotation and not used in Spock. The equivalent really is the setup() method.static. You can set a static or @Shared field in the setupSpec() method. In that case you do not need to use the closures @Vampire explained to you. You only need them if for whatever reason you really want to defer execution until after the setup() method has finished running, e.g. because your static field changes its value in between features (test methods) or iterations.And not to forget, try to avoid static fields, better use the @Shared ones. :-)
Thank you for quick and detailed reply!
Using closure like { foo(bar) } in where: block for my use-case solves my problem in a more-less clean way.
Unfortunately, using @Shared variable for the described case doesn't help.
We can close this ticket :)
No, @Shared alone will not help, but it should almost always be preferred over static, that was a general advice. :-)
Most helpful comment
That's actually expected.
The
whereblock is extracted and evaluated before the tests run.You also don't know how many iterations you would have.
Maybe you didn't notice, but if you duplicate your last line in the
whereblock, even bothfoocalls are made before the first setup method.The data table syntax you use is semantically identical to
actual << [foo(text)]or in your example evenactual = foo(text).If you would use some lazy iterable as data provider instead of a simple list (for which the table syntax is just syntactic sugar),
that only evaluates the method when the next value is requested, the methods would not be executed all before the first
setupinvocation, but still each method would be invoked before thesetupmethod for its iteration, because first the data provider has to provide the next value to see that there is actually another iteration to execute.To get what you want you could for example delay the method invocation by wrapping it in a closure that you then call within the feature method invocation, for example like this: