Having the following global extension
public class StringInjectorExtension implements IGlobalExtension {
@Override
public void start() {
}
@Override
public void visitSpec(SpecInfo spec) {
spec.allFeatures.featureMethod*.addInterceptor(new IMethodInterceptor() {
@Override
void intercept(IMethodInvocation invocation) throws Throwable {
def parameterCount = invocation.method.reflection.parameterCount
if (parameterCount > invocation.arguments.length) {
def newArguments = new Object[parameterCount]
System.arraycopy invocation.arguments, 0, newArguments, 0, invocation.arguments.length
invocation.arguments = newArguments
}
invocation.method.reflection.parameterTypes.eachWithIndex{ type, i ->
if (String.equals(type)) {
invocation.arguments[i] = 'foo: ' + System.currentTimeMillis()
}
}
invocation.proceed()
}
})
}
@Override
public void stop() {
}
}
and the specification
class TestSpecification extends Specification {
// does not compile because of
// "Data variable 'a' needs to be declared as method parameter"
// and
// "Data variable 'b' needs to be declared as method parameter"
// and
// "Parameter 'string' does not refer to a data variable"
@Unroll
def 'test1 | a = #a | b = #b'(String string) {
given:
println "string = $string"
println "a = $a"
println "b = $b"
expect:
string.startsWith('foo')
a in ['a1', 'a2']
b in ['b1', 'b2']
where:
a << ['a1', 'a2']
b << ['b1', 'b2']
}
// does not compile because of
// "Parameter 'string' does not refer to a data variable"
@Unroll
def 'test2 | a = #a | b = #b'(String string, def a, def b) {
given:
println "string = $string"
println "a = $a"
println "b = $b"
expect:
string.startsWith('foo')
a in ['a1', 'a2']
b in ['b1', 'b2']
where:
a << ['a1', 'a2']
b << ['b1', 'b2']
}
// does not compile because of
// "Parameter 'string' does not refer to a data variable"
@Unroll
def 'test3 | a = #a | b = #b'(def a, def b, String string) {
given:
println "string = $string"
println "a = $a"
println "b = $b"
expect:
string.startsWith('foo')
a in ['a1', 'a2']
b in ['b1', 'b2']
where:
a << ['a1', 'a2']
b << ['b1', 'b2']
}
// does not compile because of
// "Parameter 'string' does not refer to a data variable"
@Unroll
def 'test4 | a = #a | b = #b'(String string, def b, def a) {
given:
println "string = $string"
println "a = $a"
println "b = $b"
expect:
string.startsWith('foo')
a in ['a1', 'a2']
b in ['b1', 'b2']
where:
a << ['a1', 'a2']
b << ['b1', 'b2']
}
// does not compile because of
// "Parameter 'string' does not refer to a data variable"
@Unroll
def 'test5 | a = #a | b = #b'(def a, def b, String string) {
given:
println "string = $string"
println "a = $a"
println "b = $b"
expect:
string.startsWith('foo')
a in ['a1', 'a2']
b in ['b1', 'b2']
where:
a << ['a1', 'a2']
b << ['b1', 'b2']
}
@Unroll
def 'test6 | a = #a | b = #b'(def a, def b, String string) {
given:
println "string = $string"
println "a = $a"
println "b = $b"
expect:
string?.startsWith('foo')
a in ['a1', 'a2']
b in ['b1', 'b2']
where:
a << ['a1', 'a2']
b << ['b1', 'b2']
string = null
}
def 'test7'(String string) {
given:
println "string = $string"
expect:
string?.startsWith('foo')
}
}
only test6 and test7 compile and run as expected.
test1 to test5 do not compile with the reasons given in the inline comment above.
This means if you want to inject custom feature method parameters (e. g. when using JMockit because of the need of mocking static calls or mocking calls in objects that are not create by the test (given JMockit would support Spock which it does not yet, but I might try to contribute this if it would work properly), or when injecting custom stuff for test (I actually tried this for a project when I found this issue)) into data-driven methods, you always have to
- declare all data variables as parameters
- declare the custom injected parameter as data variable (value doesn't matter as the custom injection happens after spock internal data variable injection)
Both points seems a bit cumbersome to use and superfluous imho.
What I would like to have as behaviour is:
- If a data variable is declared via
where: block
- if the data variable is declared as method parameter (matched via name), inject the data variable into that method parameter
- if the data variable is not declared as method parameter (matched via name), inject the data variable into an automatically added method parameter
- if there are method parameters that are not declared as data variables, do not fail the compilation but inject
null, or the datatype default value (null for objects, 0 for numerics, false for booleans, ...), expecting that some extension injects the correct value for execution
Having the following global extension
and the specification
only
test6andtest7compile and run as expected.test1totest5do not compile with the reasons given in the inline comment above.This means if you want to inject custom feature method parameters (e. g. when using JMockit because of the need of mocking static calls or mocking calls in objects that are not create by the test (given JMockit would support Spock which it does not yet, but I might try to contribute this if it would work properly), or when injecting custom stuff for test (I actually tried this for a project when I found this issue)) into data-driven methods, you always have to
Both points seems a bit cumbersome to use and superfluous imho.
What I would like to have as behaviour is:
where:blocknull, or the datatype default value (nullfor objects,0for numerics,falsefor booleans, ...), expecting that some extension injects the correct value for execution