spock: Cannot create mock due to module java.base does not "opens java.lang.invoke"
Describe the bug
org.spockframework.mock.runtime.DefaultMethodInvoker is accessing java.lang.invoke.MethodHandles$Lookup.IMPL_LOOKUP which fails with:
org.spockframework.mock.CannotCreateMockException: Cannot create mock for class jdk.proxy2.$Proxy25Failed to invoke default method 'someDefaultMethod'
at org.spockframework.mock.runtime.DefaultMethodInvoker.respond(DefaultMethodInvoker.java:64)
at org.spockframework.mock.runtime.MockInvocation.callRealMethod(MockInvocation.java:59)
at org.spockframework.mock.CallRealMethodResponse.respond(CallRealMethodResponse.java:30)
at org.spockframework.mock.runtime.MockController.handle(MockController.java:50)
at org.spockframework.mock.runtime.JavaMockInterceptor.intercept(JavaMockInterceptor.java:84)
at org.spockframework.mock.runtime.DynamicProxyMockInterceptorAdapter.invoke(DynamicProxyMockInterceptorAdapter.java:34)
Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make field static final java.lang.invoke.MethodHandles$Lookup java.lang.invoke.MethodHandles$Lookup.IMPL_LOOKUP accessible: module java.base does not "opens java.lang.invoke" to unnamed module @3d99d22e
at org.spockframework.mock.runtime.DefaultMethodInvoker.respond(DefaultMethodInvoker.java:50)
... 7 more
To Reproduce
This is a minimal reproducible example minimal-reproducible-example.zip
Expected behavior
The code should not fail.
Actual behavior
The code fails with the given exception.
Java version
openjdk version “17.0.1” 2021-10-19 LTS OpenJDK Runtime Environment Corretto-17.0.1.12.1 (build 17.0.1+12-LTS) OpenJDK 64-Bit Server VM Corretto-17.0.1.12.1 (build 17.0.1+12-LTS, mixed mode, sharing) (and probably Java 16)
Buildtool version
Apache Maven 3.8.1 (05c21c65bdfed0f71a2f2ada8b84da59348c4c5d) Maven home: /Users/user/.sdkman/candidates/maven/current Java version: 17.0.1, vendor: Amazon.com Inc., runtime: /Users/user/.sdkman/candidates/java/17.0.1.12.1-amzn Default locale: en_US, platform encoding: UTF-8 OS name: “mac os x”, version: “12.0.1”, arch: “x86_64”, family: “mac”
What operating system are you using
Mac
Dependencies
com.example:demo:jar:0.0.1-SNAPSHOT
+- org.springframework.boot:spring-boot-starter:jar:2.6.2:compile
| +- org.springframework.boot:spring-boot:jar:2.6.2:compile
| | \- org.springframework:spring-context:jar:5.3.14:compile
| | +- org.springframework:spring-aop:jar:5.3.14:compile
| | +- org.springframework:spring-beans:jar:5.3.14:compile
| | \- org.springframework:spring-expression:jar:5.3.14:compile
| +- org.springframework.boot:spring-boot-autoconfigure:jar:2.6.2:compile
| +- org.springframework.boot:spring-boot-starter-logging:jar:2.6.2:compile
| | +- ch.qos.logback:logback-classic:jar:1.2.9:compile
| | | \- ch.qos.logback:logback-core:jar:1.2.9:compile
| | +- org.apache.logging.log4j:log4j-to-slf4j:jar:2.17.0:compile
| | | \- org.apache.logging.log4j:log4j-api:jar:2.17.0:compile
| | \- org.slf4j:jul-to-slf4j:jar:1.7.32:compile
| +- jakarta.annotation:jakarta.annotation-api:jar:1.3.5:compile
| +- org.springframework:spring-core:jar:5.3.14:compile
| | \- org.springframework:spring-jcl:jar:5.3.14:compile
| \- org.yaml:snakeyaml:jar:1.29:compile
+- org.springframework.boot:spring-boot-starter-test:jar:2.6.2:test
| +- org.springframework.boot:spring-boot-test:jar:2.6.2:test
| +- org.springframework.boot:spring-boot-test-autoconfigure:jar:2.6.2:test
| +- com.jayway.jsonpath:json-path:jar:2.6.0:test
| | +- net.minidev:json-smart:jar:2.4.7:test
| | | \- net.minidev:accessors-smart:jar:2.4.7:test
| | | \- org.ow2.asm:asm:jar:9.1:test
| | \- org.slf4j:slf4j-api:jar:1.7.32:compile
| +- jakarta.xml.bind:jakarta.xml.bind-api:jar:2.3.3:test
| | \- jakarta.activation:jakarta.activation-api:jar:1.2.2:test
| +- org.assertj:assertj-core:jar:3.21.0:test
| +- org.hamcrest:hamcrest:jar:2.2:test
| +- org.junit.jupiter:junit-jupiter:jar:5.8.2:test
| | +- org.junit.jupiter:junit-jupiter-api:jar:5.8.2:test
| | | +- org.opentest4j:opentest4j:jar:1.2.0:test
| | | +- org.junit.platform:junit-platform-commons:jar:1.8.2:test
| | | \- org.apiguardian:apiguardian-api:jar:1.1.2:test
| | \- org.junit.jupiter:junit-jupiter-params:jar:5.8.2:test
| +- org.mockito:mockito-core:jar:4.0.0:test
| | +- net.bytebuddy:byte-buddy:jar:1.11.22:test
| | +- net.bytebuddy:byte-buddy-agent:jar:1.11.22:test
| | \- org.objenesis:objenesis:jar:3.2:test
| +- org.mockito:mockito-junit-jupiter:jar:4.0.0:test
| +- org.skyscreamer:jsonassert:jar:1.5.0:test
| | \- com.vaadin.external.google:android-json:jar:0.0.20131108.vaadin1:test
| +- org.springframework:spring-test:jar:5.3.14:test
| \- org.xmlunit:xmlunit-core:jar:2.8.4:test
+- org.spockframework:spock-spring:jar:2.1-M2-groovy-3.0:test
| +- org.spockframework:spock-core:jar:2.1-M2-groovy-3.0:test
| | \- org.junit.platform:junit-platform-engine:jar:1.8.2:test
| \- org.codehaus.groovy:groovy:jar:3.0.9:test
+- org.codehaus.groovy:groovy-json:jar:3.0.9:test
\- org.codehaus.groovy:groovy-xml:jar:3.0.9:test
Additional context
Spock users can solve the issue by adding the following to the pom.xml:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>
--add-opens=java.base/java.lang.invoke=ALL-UNNAMED
</argLine>
</configuration>
</plugin>
But I guess a permanent fix would be nice in the library.
About this issue
- Original URL
- State: closed
- Created 3 years ago
- Comments: 29 (27 by maintainers)
Commits related to this issue
- Add smoke test for #1406 The smoke test lives in JavaSpies and reproduces the reported issue on Java 16+ for Spock 2.1-M2. It should however pass with the bugfix in this branch. — committed to kriegaex/spock by kriegaex 2 years ago
- Replace gradle-node-plugin with own tasks Removing gradle-node-plugin cuts the project's dependency footprint slightly, and enables Windows builds to succeed. Executing `pnpm` from a Gradle task pro... — committed to mbland/tomcat-servlet-testing-example by mbland 6 months ago
Java 17 offers a new method for default invocation.
This method can theoretically also be used from Byte Buddy (or cglib) proxies. Byte Buddy does however gracefully handle this when using a
@SuperCallproxy or similar as long as the default method is not ambiguous. I’d suggest to only use the reflection hack if the above official approach is not available as a fallback.You can
Spyon interfaces, this is only useful if it hasdefaultmethods that you don’t want to mock, but use as-is.As for the other matter, I’m not aware of a way to fix this in a library. If you are using the module system, then you’ll have to open the
basemodule accordingly.Just one more bit of background information for your understanding, if you are interested in such details:
jdk.proxy2.$Proxy25from the error message.It looks as if in this case Spock is first creating an interface mock and then trying to spy on it. This feels wrong to me, if it proves to be true. What Spock could do instead is to either throw a comprehensive error message or force the creation of a Byte Buddy (or CGLIB) proxy in this case in order to avoid the error message. But actually, I think the use case is simply invalid and should have been forbidden all along.
I am leaving it up to a committer to answer the last question.