When Spock's @Shared annotation should be preferred over a static field?
Solution 1
Spock is all about expressiveness and clarity.
Static is a Java keyword that only shows the internals of the class (that this field is the same for all instances)
@Shared is a Spock feature that says to the reader that this variable is the same for all feature methods. It is an instruction specifically for the unit test and makes the unit test more clear for the reader.
The same can be said for the main Spock blocks. If you think about it they do not really change anything on the code.
public void myScenario(){
int a = 2 + 3;
assertEquals(5,a);
}
public void "simple addition scenario"(){
when: "I add two numbers"
int a = 2 +3
then: "I expect the correct result"
a == 5
}
Both unit tests do exactly the same thing technically. The second however is showing more clearly the intention. The when: and then: labels do not really do anything with the code other than clarifying its intent.
So to sum up, @Shared is making the test more readable. (See also @Issue, @Title etc., they exist for the same purpose)
Solution 2
Contrary to JUnit, where you have to declare field variable static and assign value to it in
@BeforeClass
public static void setupClass()
so it was initialized just once per test suite (not each method), in Spock you may use instance field variable and annotate it with @Shared
.
Consider the following example:
class SharedTestSpec extends spock.lang.Specification {
@Shared
def shared = shared()
def shared() {
"I came from ${this.class.simpleName}"
}
def 'Test one'() {
given:
println("test one, shared: $shared")
expect: true
}
def 'Test two'() {
given:
println("test two, shared: $shared")
expect: true
}
}
class SubclassSpec extends SharedTestSpec {
@Override
def shared() {
println("They've got me!")
"I came from ${this.class.simpleName}"
}
}
Running SubclassSpec gives you the following output:
test one, shared: I came from SubclassSpec
test two, shared: I came from SubclassSpec
They've got me!
Can't explain the print order though, but that's due to AST.
Solution 3
As a more exhaustive approach, here is a sample test with outputs:
@Unroll
class BasicSpec extends Specification {
int initializedVariable
int globalVariable = 200
static int STATIC_VARIABLE = 300
@Shared
int sharedVariable = 400
void setup() {
initializedVariable = 100
}
void 'no changes'() {
expect:
printVariables()
/*
initializedVariable: 100
globalVariable: 200
STATIC_VARIABLE: 300
sharedVariable: 400
*/
}
void 'change values'() {
setup:
initializedVariable = 1100
globalVariable = 1200
STATIC_VARIABLE = 1300
sharedVariable = 1400
expect:
printVariables()
/*
initializedVariable: 1100
globalVariable: 1200
STATIC_VARIABLE: 1300
sharedVariable: 1400
*/
}
void 'print values again'() {
expect:
printVariables()
/*
initializedVariable: 100
globalVariable: 200
STATIC_VARIABLE: 1300
sharedVariable: 1400
*/
}
private void printVariables() {
println "initializedVariable: $initializedVariable"
println "globalVariable: $globalVariable"
println "STATIC_VARIABLE: $STATIC_VARIABLE"
println "sharedVariable: $sharedVariable\n"
}
}
The surprising thing to me is that both the variable in the class' setup()
method AS WELL as the global, instanced variable get reset on each test (presumably because the class is re-instantiated for each test case). Meanwhile, the static
and the @Shared
variable work as expected. As a result, the latter two are also able to be accessed in where
clauses, which are run before some of the other ones that are listed prior in each test case.
Solution 4
Static fields should only be used for constants. Otherwise shared fields are preferable, because their semantics with respect to sharing are more well-defined.
Related videos on Youtube
topr
Updated on February 19, 2021Comments
-
topr about 3 years
There is not much to add, the whole question is in the title.
Consider these two instances of class Foo used in a Spock specification.
@Shared Foo foo1 = new Foo() static Foo foo2 = new Foo()
Overall, I know the idea behind
@Shared
annotation but I guess it's better to use language features, which in this case would bestatic
field.Are there any specific cases in which one should preferred over the other or it's rather a matter of taste?
-
tim_yates about 8 yearsAfaik they're effectively the same functionally, but
@Shared
better shows your intent
-
-
topr about 8 yearsWell, there is slight difference in having and not having 'then' block. Asserts are implicit on any non-void line in such a block. But agreed, the most value comes in readability. Thanks for the answer, I'm up-voting it but going to wait a bit more to see if any other will appear. I'm keen to see if there is any other no 'syntactic sugar' reason to prefer @Shared over static (or opposite).
-
topr over 7 yearsInteresting one, thanks. To sum up both answers:
@Shared
makes specification more readable and allows advantage of an instance based methods (like overriding). -
Valya over 6 yearsInstance variables are instantiated for each
@Test
case, no wonder, same for JUnit. Can you elaborate the last sentence? I don't get it.