spock
spock copied to clipboard
Global Groovy mock doesn't work as expected
Hello, I made a unit test example based on the documentation here: http://spockframework.org/spock/docs/1.1/interaction_based_testing.html
Specifically, I am following the example under the section: Mocking All Instances of a Type
Here is my code:
package library
import spock.lang.Specification
class Publisher {
def subscribers = []
def send(event) {
subscribers.each {
it.receive(event)
}
}
}
interface Subscriber {
def receive(event)
}
class RealSubscriber implements Subscriber {
@Override
def receive(Object event) {
return null
}
}
class PublisherSpec extends Specification {
def "delivers events to all subscribers"() {
given:
def pub = new Publisher()
def sub1 = Mock(Subscriber)
def sub2 = Mock(Subscriber)
pub.subscribers << sub1 << sub2
when:
pub.send("event")
then:
1 * sub1.receive("event")
1 * sub2.receive("event")
}
def "test global mock"() {
given:
def pub = new Publisher()
pub.subscribers << new RealSubscriber() << new RealSubscriber()
def anySub = GroovyMock(RealSubscriber, global:true)
when:
pub.send("event")
then:
2 * anySub.receive("event")
}
}
After running these 2 tests, the first one passes. However the second one failed with below error:
Too few invocations for:
2 * anySub.receive("event") (0 invocations)
Unmatched invocations (ordered by similarity):
None
at org.spockframework.mock.runtime.InteractionScope.verifyInteractions(InteractionScope.java:78)
at org.spockframework.mock.runtime.MockController.leaveScope(MockController.java:76)
at library.PublisherSpec.test global mock(PublisherSpec.groovy:51)
I would expect the second test passes as well, did I miss something in the test? Please help!
Here is my local env: (and I am using latest spock: spock-core:1.1-groovy-2.4-rc-3)
Gradle 4.2
Build time: 2017-09-20 14:48:23 UTC Revision: 5ba503cc17748671c83ce35d7da1cffd6e24dfbd
Groovy: 2.4.11 Ant: Apache Ant(TM) version 1.9.6 compiled on June 29 2015 JVM: 1.8.0_102 (Oracle Corporation 25.102-b14) OS: Linux 4.10.0-35-generic amd64
I also find a weird situation when I try to use global GroovySpy. I copied the unit test example here: https://github.com/spockframework/spock/blob/master/spock-specs/src/test/groovy/org/spockframework/smoke/mock/GroovySpiesThatAreGlobal.groovy and I modify it a litter bit. Here is my code example:
def "mock dynamic instance method called via MOP and it doesn't work"() {
def person = new Person()
def anyPerson = GroovySpy(Person, global: true)
when:
person.invokeMethod("foo", [42] as Object[])
then:
1 * anyPerson.foo(42) >> "done"
}
The above test case failed which gives me this error:
groovy.lang.MissingMethodException: No signature of method: static library.GroovySpiesThatAreGlobal.foo() is applicable for argument types: (java.lang.Integer) values: [42] Possible solutions: Mock(), Spy(), any(), find(), Mock(groovy.lang.Closure), Mock(java.lang.Class)
at library.GroovySpiesThatAreGlobal.mock dynamic instance method called via MOP and it doesn't work(GroovySpiesThatAreGlobal.groovy:125)
The only thing I changed is the order of mock and actual creation of the instance. Can someone explain why this test case fails?
I have similar issue:
def someData = "cba"
def "test something"() {
given: "some initial data"
def initialData = "abc"
....
when: "calling test method"
def result = testClass.testMethod(initialData)
then:
1 * someMockObject.callSomeMethod(initialData) >> someData
}
but when I try to execute such test case -- mock behaviour defined in then clause not working and someMockObject.callSomeMethod() always return null. Spock version is 1.3-groovy-2.5
@flyingclamking So I guess the issue is, that you are creating the new Person()
before the GroovySpy(Person, global: true)
and that an instance inherits its MetaClass during construction. This means your Person is getting the original "real" Groovy MetaClass
.
So the GroovySpy
can only change instances, which were not already created.
Every other Person
object which will be created after the GroovySpy
call, will get the GroovyMockMetaClass
, which will implement the mocking behavior. And this gets reverted in the cleanup phase.
Also to your first example, if you create the pub.subscribers << new RealSubscriber() << new RealSubscriber()
after you are doing the GroovySpy(). It will work just fine.
Note: You are using an GroovyMock
above, which will answer everything with null
incl. the Constructor, so you can't just switch the lines, you also need to change it from GroovyMock
to GroovySpy
to use the constructor of RealSubscriber
.
@leonard84 Maybe we should state in the documentation that the order of declaration for a global:true
GroovyMock/Stub/Spy is relevant, that the MetaClass
changes takes effect.