Skip to content

Mock Groovy Object expecteding "get" rather than "is" with dot notation accessor #1256

@philippreston

Description

@philippreston

Issue description

In our testing of a groovy / grails application we have an object under test, into which we inject a Mock of an object, to mock out a method that is called. In our migration to spock 2 (org.spockframework:spock-core:2.0-M4-groovy-3.0) we have hit an issue when the mock object has a method that returns a boolean - the method cannot be found when accessed by groovy's dot notation accessor.

When the dot notation is used for a boolean it looks for a method with "get" as the method prefix rather than "is". I have recreated this in a standalone groovy script (which I will link to). So the Mock is an mock of a real object, which the class is:

class ExampleData {

    boolean isCurrent() {
        true
    }
}

When this ExampleData is created as a Mock, its then called in the controller as follows:

    def show1() {
       data.current ? "Data is current" : "Data is not current"
    }

When this is called - we get the following error:

JUnit5 launcher: passed=1, failed=1, skipped=0, time=502ms

Failures (1):
  Spock:TestV3:Test Mock Object Boolean accessor via dot-notation
    MethodSource [className = 'TestV3', methodName = 'Test Mock Object Boolean accessor via dot-notation', methodParameterTypes = '']
    => groovy.lang.MissingMethodException: No signature of method: ExampleData.getCurrent() is applicable for argument types: () values: []
Possible solutions: isCurrent()
       ExampleController.show1(testV3.groovy:61)
       TestV3.Test Mock Object Boolean accessor via dot-notation(testV3.groovy:23)
Caught: groovy.lang.GroovyRuntimeException
groovy.lang.GroovyRuntimeException
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
Caused by: groovy.lang.MissingMethodException: No signature of method: ExampleData.getCurrent() is applicable for argument types: () values: []
Possible solutions: isCurrent()
	at ExampleController.show1(testV3.groovy:61)
	at TestV3.Test Mock Object Boolean accessor via dot-notation(testV3.groovy:23)

So you can see when accessed via dot notation then you can see that its looking for getCurrent() rather than isCurrent()

How to reproduce

So this can be reproduced with the following groovy code:

@Grab('cglib:cglib-nodep:3.3.0')
@Grab('org.spockframework:spock-core:2.0-M4-groovy-3.0')
@GrabExclude('org.codehaus.groovy:*')
import spock.lang.Shared
import spock.lang.Specification


class TestV3 extends Specification {

    @Shared 
    ExampleData mockData

    def setup() {
        mockData = Mock(ExampleData) 
    }

    def "Test Mock Object Boolean accessor via dot-notation" () {

        given: "Test Controller"
        ExampleController controller = new ExampleController(data: mockData)

        when: "call test method"
        def result = controller.show1()

        then: "should call mock data"
        1 * mockData.isCurrent() >> true

        and: "should equal"
        result == "Data is current"
    }

    def "Test Mock Object Boolean accessor via method" () {

        given: "Test Controller"
        ExampleController controller = new ExampleController(data: mockData)

        when: "call test method"
        def result = controller.show2()

        then: "should call mock data"
        1 * mockData.isCurrent() >> true

        and: "should equal"
        result == "Data is current"
    }
}

class ExampleData {

    boolean isCurrent() {
        true
    }
}


class ExampleController {

    ExampleData data

    def show1() {
       data.current ? "Data is current" : "Data is not current" 
    }

    def show2() {
       data.isCurrent() ? "Data is current" : "Data is not current" 
    }

}

To run:

groovy testV3

You will see the error as per the description.

I can show that this works with previous version of spock by changing the dependency:

 diff testV2.groovy testV3.groovy
2c2
< @Grab('org.spockframework:spock-core:1.3-groovy-2.5')
---
> @Grab('org.spockframework:spock-core:2.0-M4-groovy-3.0')
8c8
< class TestV2 extends Specification {
---
> class TestV3 extends Specification {

and running with groovy v2. This works

Additional Environment information

Java/JDK

openjdk version "11.0.8" 2020-07-14 LTS
OpenJDK Runtime Environment Zulu11.41+23-CA (build 11.0.8+10-LTS)
OpenJDK 64-Bit Server VM Zulu11.41+23-CA (build 11.0.8+10-LTS, mixed mode)

Groovy version

Groovy Version: 3.0.4 JVM: 11.0.8 Vendor: Azul Systems, Inc. OS: Mac OS X

Build tool version

Grapes

Operating System

Mac OS X 10.15.5

Build-tool dependencies used

Groovy Grape

@Grab('cglib:cglib-nodep:3.3.0')
@Grab('org.spockframework:spock-core:2.0-M4-groovy-3.0')
@GrabExclude('org.codehaus.groovy:*')

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions