spock icon indicating copy to clipboard operation
spock copied to clipboard

GroovyMock object on a final class doesn't work properly inside statically compiled service

Open timic opened this issue 8 years ago • 4 comments

Issue description

GroovyMock interactions for final class doesn't work inside@CompileStatic annotated class or in plain java class. In this case mock object will use real method implementation instead of provided interaction.

Minimal code to reproduce

import groovy.transform.CompileStatic
import spock.lang.Specification

class UserServiceSpec extends Specification {
    def "test final class mock interactions"() {
        setup:
            def mock = GroovyMock(FinalClass) {
                getName() >> "Foo Bar"
            }
        when:
            new StaticService().getName(mock)
        then:
            thrown(UnsupportedOperationException)
        expect:
            new GroovyService().getName(mock) == "FOO BAR"
    }
}

final class FinalClass {
    String getName() {
        throw new UnsupportedOperationException("Not implemented")    
    }
}

class GroovyService {
    String getName(FinalClass obj) {
        obj.name.toUpperCase()
    }
}

@CompileStatic
class StaticService {
    String getName(FinalClass obj) {
        obj.name.toUpperCase()
    }
}

Environment

Java: OpenJDK Runtime Environment (build 1.8.0_144-b01) Gradle: 3.5

Gradle dependencies

compile 'org.codehaus.groovy:groovy-all:2.4.12'
compile 'org.spockframework:spock-core:1.1-groovy-2.4'
compile 'cglib:cglib-nodep:3.2.5'

timic avatar Sep 07 '17 13:09 timic

GroovyMocks work via modification of the meta-class. Using static compilation will basically turn groovy bytecode into java equivalent bytecode, e.g. static binding and thus no meta-class magic.

leonard84 avatar Sep 08 '17 00:09 leonard84

@leonard84 In this case GroovyMock works well when making FinalClass non-final. Also there is no matter whether CompileStatic over FinalClass is present or not.

timic avatar Sep 08 '17 10:09 timic

@timic yes, I don't think there is anything we can do about it.

The bytecode generated for StaticService is the same whether FinalClass is indeed final or not, it uses invokevirtual in both cases. I think that the JVM is optimizing the code during load/execution, since the class is final, and inlines the address. However, I'm not an expert on this matter.

leonard84 avatar Sep 11 '17 20:09 leonard84

I have just encountered this problem as well!

passionfruit18 avatar Sep 27 '19 10:09 passionfruit18