Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Global Groovy mock doesn't work as expected #785

Closed
flyingclamking opened this issue Oct 27, 2017 · 3 comments · Fixed by #1755
Closed

Global Groovy mock doesn't work as expected #785

flyingclamking opened this issue Oct 27, 2017 · 3 comments · Fixed by #1755
Labels

Comments

@flyingclamking
Copy link

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

@flyingclamking
Copy link
Author

flyingclamking commented Oct 27, 2017

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?

@leonard84 leonard84 added the bug label Jul 7, 2018
@lyoumi
Copy link

lyoumi commented Feb 19, 2021

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

@AndreasTu
Copy link
Member

@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.

AndreasTu added a commit to AndreasTu/spock that referenced this issue Aug 12, 2023
Added test case base documentation for global mocks.
And clarify the usage of the global mock and lifecycle.

This fixes spockframework#785
leonard84 pushed a commit that referenced this issue Sep 15, 2023
Added test case base documentation for global mocks. And clarify the usage of the global mock and lifecycle.

This fixes #785
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants