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

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

Closed
philippreston opened this issue Jan 11, 2021 · 8 comments · Fixed by #1257
Closed

Comments

@philippreston
Copy link

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:*')
@kriegaex
Copy link
Contributor

kriegaex commented Jan 11, 2021

Good catch! I can confirm this, provided that the class under test (CUT) is a (stand-alone) Groovy class. The problem does not occur if the CUT is a Java class or a static inner class of the specification. I can also confirm that in Spock 1.3 there is no issue while in 2.0-M4 there is one.

leonard84 added a commit that referenced this issue Jan 22, 2021
fixes #1256

Co-authored-by: Alexander Kriegisch <Alexander@Kriegisch.name>
@mlasevich
Copy link

@kriegaex - FWIW, it appears I am running into the same issue with org.spockframework:spock-core:1.3-groovy-2.4 - so the issue does seem to be present on 1.3 as well

@kriegaex
Copy link
Contributor

Probably it will never be fixed in 1.3, but I would like to see a reproducer anyway because when I tested it, there was no problem. Maybe there is a corner case which should also be checked in 2.0.

@mlasevich
Copy link

Here it is:

import spock.lang.Specification

class TesterSpec extends Specification {

  static class Tester {

    boolean isEnabled() {
      return true
    }
  }

  def 'Works'() {
    given:
      Tester x = new Tester()
    expect:
      x.enabled == true
  }

  def 'GroovySpy does not work Test'() {
    given:
      Tester x = GroovySpy(Tester)
    expect:
      x.enabled == true
  }

  def 'GroovyMock does not work either Test'(){
    given:
      Tester x = GroovyMock(Tester)
    when:
      x.enabled
    then:
      1 * x.isEnabled() >> true
  }

  def 'Mock works though Test'(){
    given:
      Tester x = Mock(Tester)
    when:
      x.enabled
    then:
      1 * x.isEnabled() >> true
  }
}

@kriegaex
Copy link
Contributor

kriegaex commented Jan 30, 2021

I am glad I asked for the MCVE.

This is a similar issue, but not exactly the same as the one fixed here. Otherwise your specification would work in Spock 2 master and fail in 1.3. It fails in both versions, though, so this is kind of an extension of the existing issue, seemingly affecting Groovy mocks/spies.

@leonard84, @Vampire, any opinions how you would like to handle this? Reopen or new issue?

@leonard84
Copy link
Member

A new issue would be better, as the OP one was for java mocks.

@mlasevich
Copy link

Opened new issue. (#1270)

@kriegaex
Copy link
Contributor

@leonard84, maybe you want to rename both issues in order to make them more easily distinguishable from each another. I do not have the necessary rights. That would also be helpful for the issue list in the next release. At the same time you could fix the little "expecteding" typo.

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

Successfully merging a pull request may close this issue.

4 participants