Using a where: clause, the type of a parameter is inferred incorrectly from the type of a local variable declared in an unrelated method when using Groovy 2.5. The issue does not occur with Groovy 2.4
Consider the following code:
import spock.lang.Specification
import spock.lang.Unroll
@Unroll
class MyTest extends Specification {
def "#a <=> #b: #result"() {
expect:
Math.signum(a <=> b) == result
where:
a | b | result
"abcdef12" | "abcdef12" | 0
}
private double[] someOtherMethod() {
double[] result = new double[0]
return result
}
}
This works fine when using Groovy 2.4, but fails when using Groovy 2.5 – both with Spock 1.0-groovy-2.4 and 1.2-RC1-groovy-2.5:
abcdef12 <=> abcdef12: [0.0]
Condition not satisfied:
Math.signum(a <=> b) == result
| | | | | |
0.0 | 0 | | [0.0]
| | false
| abcdef12
abcdef12
at MyTest.#a <=> #b: #result(MyTest.groovy:8)
Notice that result's value is printed as [0.0], which matches the type of the local variable result defined in someOtherMethod(). If that method is removed, or the result parameter in the test is renamed, the test works as expected.
Here's a full build to reproduce the problem: https://github.com/lptr/spock-groovy-bug
Just run ./gradlew test
Gradle 4.9, Groovy 2.5.2, Spock 1.2-RC1
@paulk-asert any idea what changed with groovy 2.5 that causes this?
@lptr my suggestion instead of changing the name of the parameter would be to explicitly declare the method parameters with their correct type.
@paulk-asert In Groovy 2.5 during AST
Phase Instruction Selection
public java.lang.Object $spock_feature_0_0proc(java.lang.Object $spock_p0, java.lang.Object $spock_p1, java.lang.Object $spock_p2) {
java.lang.Object a = $spock_p0
java.lang.Object b = $spock_p1
java.lang.Object result = $spock_p2
return new java.lang.Object[]
}
Phase Class Generation
public java.lang.Object $spock_feature_0_0proc(java.lang.Object $spock_p0, java.lang.Object $spock_p1, java.lang.Object $spock_p2) {
java.lang.Object a = $spock_p0
java.lang.Object b = $spock_p1
[D result = $spock_p2
return new java.lang.Object[]
}
While in 2.4 it stays as java.lang.Object
@leonard84 Nothing springs to mind but looks like a bug somewhere. I'll take a look next week.
Encountered the same problem with Griffon 3.x. Workaround was to rename the variable https://github.com/griffon/griffon/commit/afed03a66db9e8d7119f692987c4bc96824e4cd6 @paulk-asert
This even occurs if parameter is typed
class DummyTest extends Specification {
@Unroll
def "roll #x"(Integer x) {
expect:
1 == x
where:
x << [1]
}
private int unused() {
String x = "4"
return 3
}
}
java.lang.IllegalArgumentException: Method '$spock_feature_0_0([class java.lang.Integer])' can't be called with parameters '[1]'!
at org.spockframework.util.ReflectionUtil.validateArguments(ReflectionUtil.java:212)
at org.spockframework.util.ReflectionUtil.invokeMethod(ReflectionUtil.java:199)
at org.spockframework.runtime.model.MethodInfo.invoke(MethodInfo.java:113)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Just bumping this issue because I am seeing this issue as well. The following test class fails with the error below:
import spock.lang.Specification
import spock.lang.Unroll
@Unroll
class TestSpec extends Specification {
void 'test test list'() {
List expectedResponse = ['foo']
}
def 'test test'() {
expect:
expectedResponse
where:
_ | expectedResponse
_ | ['message': "no products found"]
}
}
Cannot cast object '{message=no products found}' with class 'java.util.LinkedHashMap' to class 'java.util.List' due to: groovy.lang.GroovyRuntimeException: Could not find matching constructor for: java.util.List(LinkedHashMap)
org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast object '{message=no products found}' with class 'java.util.LinkedHashMap' to class 'java.util.List' due to: groovy.lang.GroovyRuntimeException: Could not find matching constructor for: java.util.List(LinkedHashMap)
at TestSpec.test test(TestSpec.groovy:18)
@paulk-asert it still happens with groovy-2.5.7 :(
Do you know if there is a reason why in the statement below, the result VariableExpression has the accessedVariable set to a dynamic variable rather than the result parameter from the $spock_feature_0_0 method? Taken from the groovyConsole AST view for above:
org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector,
$spock_valueRecorder.reset(), 'Math.signum(a <=> b) == result', 9, 9, null,
$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(8),
$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(6),
java.lang.Math.$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1),
'signum')($spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(4),
$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), a) <=>
$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(3), b)))) ==
$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(7), result)))
@paulk-asert not really, that is rather old code that I've not written. The code is generated via org.spockframework.compiler.ConditionRewriter#record.
@leonard84 is there any progress on this or maybe some workaround (except avoiding variable name equality)?
Also suffering from this…
I'd really like to migrate from Groovy 2.4.x to 2.5.x but this issue is a showstopper. We have a lot of spock based tests which break because of this.
:-(
This StackOverflow question also describes the same problem and points to this ticket. It has been open for 14+ months now and I think it ought to be addressed soon, especially given the fact that Groovy 3 is also affected.
There are enough tests to cover this, but FWIW let me just say that I also re-tested the current master against the code from the SO question I mentioned earlier and the issue there is fixed for that case. Thanks, @Vampire.
Most helpful comment
This StackOverflow question also describes the same problem and points to this ticket. It has been open for 14+ months now and I think it ought to be addressed soon, especially given the fact that Groovy 3 is also affected.