Not able to use global GroovyMock in setupSpec()
Issue description
When trying to setup some class globally using GroovyMock(i.e. GroovyMock(StringUtils, global: true)) in setupSpec, Spock throws exception saying:
java.lang.IllegalStateException: Cannot request current iteration in @Shared context.
After digging a little bit more, I found out that this is thrown here: https://github.com/spockframework/spock/blob/6ca84d59da57a0186910daf88fce7d5e1cbb2dfa/spock-core/src/main/java/org/spockframework/mock/runtime/GroovyMockFactory.java#L50 And by reading the code, it seems to be caused by the fact that Spock is trying to attach a cleanup process to iteration which is failed because setupSpec is outside any test iteration.
I'm not entirely sure if this is as designed or a bug. I couldn't find anything on the documentation saying you are not supposed to use GroovyMock in setupSpec. Documentation is only specifying that you are not supposed to reference instance fields unless they are annotated with @Shared which is clearly not the case here.
So I'm reporting it here and hoping it can be either fixed or at least the documentation be made clearer that this is a limitation one should not be doing.
How to reproduce
For example a simple test case like this:
class SomeTest extends Specification{
def setupSpec() {
GroovyMock(StringUtils, global: true)
StringUtils.isEmpty(_) >> false
}
def "some test"() {
expect:
!StringUtils.isEmpty("")
}
}
Will fail with the following message:
java.lang.IllegalStateException: Cannot request current iteration in @Shared context
at org.spockframework.mock.runtime.GroovyMockFactory.create(GroovyMockFactory.java:49)
at org.spockframework.mock.runtime.CompositeMockFactory.create(CompositeMockFactory.java:44)
at org.spockframework.lang.SpecInternals.createMock(SpecInternals.java:45)
at org.spockframework.lang.SpecInternals.createMockImpl(SpecInternals.java:281)
at org.spockframework.lang.SpecInternals.GroovyMockImpl(SpecInternals.java:203)
Additional Environment information
Java/JDK
java version "1.8.0_151"
Groovy version
groovy 2.4.12
Operating System
Mac 10.12.6
IDE
Intellij
It is not a bug, but maybe it should be mentioned somewhere. Mocks are always bound to a Specification instance and should not be shared. The setupSpec runs in a shared context and thus it fails. Do you have a specific use case why it would be necessary, or was this just confusion about why it doesn't work?
Just move your mock to setup and everything should work fine.
@leonard84 thanks for the explanation. That's what I thought as well. And I strongly agree with this should be mentioned somewhere in the documentation. Also the exception here should be clearer in reflecting what the real problem is. Right now saying java.lang.IllegalStateException: Cannot request current iteration in @Shared context. is very misleading
The use case is I cannot export my mocking methods to a test utils class to share with other tests.. For example I have this method in my test class and it works fine. If I want to move it to a test utils, I have to create another class which excends Specification otherwise a static method isn't working.
def mockGet(url, int times, jql) {
times * Unirest.get(url) >> Mock(GetRequest) {
header(_, _) >> it
asObject(_) >> Mock(HttpResponse) {
status >> 200
body >> [
jql: jql
]
}
}
}