generator-jhipster: Error while using @MockBean in integration test
Overview of the issue
I created a new integration test Class to test one of my service implementation. I use @MockBean to mock other beans. When I launch only this test Class everything works fine. However when I run all tests with JUnit or Maven verify the tests running after my test have errors they do not have when the @MockBean is not present
The exception thrown in the other tests seems linked to the cache
Exception
java.lang.IllegalStateException at com.github.benmanes.caffeine.jcache.CacheProxy.requireNotClosed(CacheProxy.java:1054) at com.github.benmanes.caffeine.jcache.CacheProxy.remove(CacheProxy.java:465) at org.springframework.cache.jcache.JCacheCache.evict(JCacheCache.java:106) at com.mycompany.myapp.service.UserService.clearUserCaches(UserService.java:292) at com.mycompany.myapp.service.UserService.lambda$completePasswordReset$2(UserService.java:73) at java.base/java.util.Optional.map(Optional.java:265) at com.mycompany.myapp.service.UserService.completePasswordReset(UserService.java:69) at com.mycompany.myapp.service.UserService$$FastClassBySpringCGLIB$$2169c3ce.invoke(<generated>) at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:750) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:295) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:689) at com.mycompany.myapp.service.UserService$$EnhancerBySpringCGLIB$$9f6eeabd.completePasswordReset(<generated>) at com.mycompany.myapp.service.UserServiceIT.assertThatUserCanResetPassword(UserServiceIT.java:145) 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) at java.base/java.lang.reflect.Method.invoke(Method.java:566) at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:532) at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:115) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:171) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:72) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:167) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:114) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:59) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$4(NodeTestTask.java:108) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:72) at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:98) at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:74) at java.base/java.util.ArrayList.forEach(ArrayList.java:1540) at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$4(NodeTestTask.java:112) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:72) at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:98) at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:74) at java.base/java.util.ArrayList.forEach(ArrayList.java:1540) at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$4(NodeTestTask.java:112) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:72) at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:98) at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:74) at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32) at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57) at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51) at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:220) at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$6(DefaultLauncher.java:188) at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:202) at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:181) at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:128) at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:69) at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47) at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242) at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Motivation for or Use Case
Using @MockBean is really powerful to test spring boot applications it simplifies the bean and mock creations in SpringBootTest
Reproduce the error
To reproduce the error I created a repo you can find here The test I created is here
To reproduce the error just clone the repo and run tests with Maven or JUnit
Related issues
I found this old issue https://github.com/jhipster/generator-jhipster/issues/5354
It seems to be the same kind of stuff. Something with Spring Context and Cache configuration when running tests.
When running test with @MockBean SpringBoot recreates a new Context but it seems to not recreate proper cache
Suggest a Fix
I have no idea how to fix the bug but if someone can explain me where does it come I can help to fix it
JHipster Version(s)
I am using 6.4.1 version with caffeine cache
JHipster configuration
.yo-rc.json file
{
"generator-jhipster": {
"promptValues": {
"packageName": "com.mycompany.myapp",
"nativeLanguage": "en"
},
"jhipsterVersion": "6.4.1",
"applicationType": "monolith",
"baseName": "tester",
"packageName": "com.mycompany.myapp",
"packageFolder": "com/mycompany/myapp",
"serverPort": "8080",
"authenticationType": "jwt",
"cacheProvider": "caffeine",
"enableHibernateCache": true,
"websocket": false,
"databaseType": "sql",
"devDatabaseType": "h2Disk",
"prodDatabaseType": "postgresql",
"searchEngine": false,
"messageBroker": false,
"serviceDiscoveryType": false,
"buildTool": "maven",
"enableSwaggerCodegen": false,
"jwtSecretKey": "xxxxxxxx",
"embeddableLaunchScript": false,
"useSass": true,
"clientPackageManager": "npm",
"clientFramework": "angularX",
"clientTheme": "none",
"clientThemeVariant": "",
"testFrameworks": [],
"jhiPrefix": "jhi",
"entitySuffix": "",
"dtoSuffix": "DTO",
"otherModules": [],
"enableTranslation": true,
"nativeLanguage": "en",
"languages": ["en"],
"blueprints": []
}
}
Entity configuration(s) entityName.json files generated in the .jhipster directory
You can get all the informations in the repo
Browsers and Operating System
Using windows 10
- Checking this box is mandatory (this is just to show you read everything)
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Comments: 17 (17 by maintainers)
Does it happen with other cache implementations? It seems the cache is closed. Which probably means the app context was closed. But then, the same cache is reused. Most probably because it wasn’t removed from JCache. So from here, it might be a Cafeine bug, a Spring Cache bug (I doubt it though) or a JHipster bug (maybe). I would check why the cache is closed and then retrieved.
Here is the full code as a PR to your test repo: https://github.com/Ckram/jhi-tester/pull/1
I did not do extensive testing.
My 2 cents
We dealt with the same error in a JHipster 6.6+ application since we added
@AutoConfigureMockMvcannotation in our integration tests. We adopt the API first design with open JHipster option and we wanted to test our API through integration tests.The UserServiceIT test class throw the following exception :
The quick and dirty fix is to add
@AutoConfigureMockMvcat the top of the classIt works ! But it’s weird.
So I have just tested the @henri-tremblay solution and I can confirm it fix the problem.
I am not able to explain the behaviour or the reason of this bug but I can tell this:
Should we do a PR to apply the last solution ?
I have dug in it a little bit. The problem seems to be that
@MockBeantaint the Spring context in some wayCacheConfigurationto be run againCacheManageris retrieved, which is expectedCacheConfigurationdestroys the cache and create a new one. All wrapped under a newJCacheCacheManagerJCacheCacheManagerare staying a dependency to other beans likeUserServiceJCacheCacheManager, it finds oldJCachelinked to oldCachethat are now destroyed. Thus the problem.A solution is to do that:
Since we are in the same context, the configuration is the same so clearing the cache should be fine.
However, it looks like a Spring bug. But I’m not sure at which level. Maybe the
JCacheCacheConfigurationshould not create a newJCacheCacheManager.It feels like a bug. From my point of view a new ApplicationContext should use a new cache manager. Or the previous one should be cleared. I don’t have the time to check right now but can in a few days.
I tried adding “spring.cache.type=none” to test
application.ymlbut it did not work.Additionally, I added a condition on
CacheConfiguration:but it failed because
UserServicedepends onCacheManagerfor clearing caches. I think thatUserServiceshould be able to work without caching.So I created a
NoCacheConfigurationclass in test to provide aNoOpCacheManagerwhich is probably what Spring Boot autoconfiguration does for “spring.cache.type=none” and all tests passed exceptUserResourceIT#getUserwhich purpose is to test caching.Some ideas also from: https://stackoverflow.com/questions/57201103/spring-boot-test-tries-to-initialize-cache2k-for-the-2nd-time-and-fails