springmockk
springmockk copied to clipboard
@SpykBean on Spring Data Repository doesn't work anymore with JDK16+
Hi,
I already stumbled over @jnizet commit https://github.com/Ninja-Squad/springmockk/commit/b6dbc1b50d4cacf72c50119fdfc5273ae5fc4677
But in our case @SpykBean
isn't working with following configuration:
- openjdk version "16.0.1" 2021-04-20
- Kotlin 1.5.21
- Spring Boot 2.5.3
- SpringMockk 3.0.1
Simple data repository spy:
@SpykBean
private lateinit var documentRepository: DocumentRepository
Exception when running the test:
...
nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'documentRepository': Post-processing of FactoryBean's singleton object failed; nested exception is java.lang.IllegalAccessException: class io.mockk.impl.InternalPlatform$copyFields$1 cannot access a member of class java.lang.reflect.Proxy (in module java.base) with modifiers "protected"
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:800)
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:229)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1354)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1204)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:564)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:524)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:944)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:918)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:145)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:434)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:338)
at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:123)
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:99)
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:124)
... 68 common frames omitted
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'documentRepository': Post-processing of FactoryBean's singleton object failed; nested exception is java.lang.IllegalAccessException: class io.mockk.impl.InternalPlatform$copyFields$1 cannot access a member of class java.lang.reflect.Proxy (in module java.base) with modifiers "protected"
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.getObjectFromFactoryBean(FactoryBeanRegistrySupport.java:119)
at org.springframework.beans.factory.support.AbstractBeanFactory.getObjectForBeanInstance(AbstractBeanFactory.java:1884)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getObjectForBeanInstance(AbstractAutowireCapableBeanFactory.java:1266)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:345)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1380)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1300)
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:887)
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:791)
... 87 common frames omitted
Caused by: java.lang.IllegalAccessException: class io.mockk.impl.InternalPlatform$copyFields$1 cannot access a member of class java.lang.reflect.Proxy (in module java.base) with modifiers "protected"
at java.base/jdk.internal.reflect.Reflection.newIllegalAccessException(Reflection.java:385)
at java.base/java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:687)
at java.base/java.lang.reflect.Field.checkAccess(Field.java:1096)
at java.base/java.lang.reflect.Field.get(Field.java:417)
at io.mockk.impl.InternalPlatform$copyFields$1.invoke(InternalPlatform.kt:110)
at io.mockk.impl.InternalPlatform$copyFields$1.invoke(InternalPlatform.kt:114)
at io.mockk.impl.InternalPlatform.copyFields(InternalPlatform.kt:117)
at io.mockk.impl.instantiation.AbstractMockFactory.spyk(AbstractMockFactory.kt:107)
at com.ninjasquad.springmockk.SpykDefinition.createSpy(SpykDefinition.kt:75)
at com.ninjasquad.springmockk.MockkPostProcessor.createSpyIfNecessary(MockkPostProcessor.kt:335)
at com.ninjasquad.springmockk.MockkPostProcessor$SpyPostProcessor.postProcessAfterInitialization(MockkPostProcessor.kt:398)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization(AbstractAutowireCapableBeanFactory.java:437)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.postProcessObjectFromFactoryBean(AbstractAutowireCapableBeanFactory.java:1929)
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.getObjectFromFactoryBean(FactoryBeanRegistrySupport.java:116)
... 96 common frames omitted
The workaround is to switch to JDK <= 15.
Has anyone an idea to fix the issue?
It doesn't seem to work on @RequestScope
@SessionScope
beans neither
Sorry folks. To be honest, I have no idea of how to deal with this issue. My guess is that it's actually a problem of MockK itself regarding spying proxies. If one of you could come up with a simple repro (as a github repo), maybe not even using SpringMockK, and could sublit this issue to MockK, that would be great.
Also met this issue while trying to update to JDK17.
Are there any options to overcome this? I thought common to spy on repositories. I'm surprised that not that many people report that they have this problem. I don't see any relevant issue on mockk side.
One option I see is not to spy on the repositories ;) But it's said nowhere in the documentation, that one shall not do that.
@jnizet I agree that this is almost certainly a mockk issue with proxy spies.
Another piece of information - for whatever reason the final exception masks the original exception:
java.lang.reflect.InaccessibleObjectException: Unable to make field protected java.lang.reflect.InvocationHandler java.lang.reflect.Proxy.h accessible: module java.base does not "opens java.lang.reflect" to unnamed module @17550481

So it appears the new Java 17 strong encapsulation is what is actually breaking this. In 9-11 this just gave a warning, now it fails hard.
@ajgassner @PavelPolyakov a workaround is to add the --add-opens
JVM flag to your test execution. In Gradle (build.gradle.kts
):
tasks.test {
jvmArgs = listOf("--add-opens=java.base/java.lang.reflect=ALL-UNNAMED")
}
Adding this for me causes my spied repositories to work just as before. My JVM:
openjdk version "17.0.2" 2022-01-18
OpenJDK Runtime Environment Temurin-17.0.2+8 (build 17.0.2+8)
OpenJDK 64-Bit Server VM Temurin-17.0.2+8 (build 17.0.2+8, mixed mode, sharing)
Thanks a lot @phillipuniverse , that's good information!
It would really be nice if you could open an issue, with a minimal repro, on the MockK repo, because it looks more and more like something that does not really come from SpringMockK.
It seems to me, spring data is also involved in this issue. According to spring docs, java.lang.reflect.Proxy
should not be used as proxy any more (by default) in favor of CGLib one (not official, but embedded in spring core).
Any further progress on this issue? I just updated to java 17 and run into the same errors. So its seem to be still an issue :(
@MarianPohling The workaround explained above, and now described in the README, is the official solution from MockK