I'm writing Spock tests, and used an inline closure to stub for simple fail/pass behaviour.
def "test timeout"() {
given:
2 * feignClient.poll("foo") >>
{
int retries = 0;
if (retries < 1) {
retries++
throw newRetryable()
}
pollWaitSuccessResponseEntity
}
So I tried to refactor the closure to a named Closure:
def retryClosure = {
int retries = 0;
if (retries < 1) {
retries++
throw newRetryable()
}
pollWaitSuccessResponseEntity
}
2 * feignClient.poll("foo") >> retryClosure
The test fails with the following error:
Caused by: org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast object
'com.example.service.FooServiceTest$__spock_initializeFields_closure2@3243178c' with class 'com.example..FooServiceTest$__spock_initializeFields_closure2' to class 'org.springframework.http.ResponseEntity'
Spock relies heavily on AST-Transformations, for that it is necessary that certain constructs are used.
1 * service.doSomething() >> x
will just return x
1 * service.doSomething() >> { x }
will run the code in the closure and return x
So, if you want to delay the execution of the response code, but still want to put it in a variable you need to wrap the execution in a closure.
def myClosure = {
otherService.foo()
}
2 * service.doSomething() >> { myClosure() }
Just know that you could use response chaining instead.
2 * feignClient.poll("foo") >> { throw newRetryable() } >> pollWaitSuccessResponseEntity
The main issue with your closure code, is that you are keeping the state inside the closure, and thus it will be reset on each invocation. You would need to move the retry counter out of the closure, so that the state would be kept between invocations.