Spring Boot测试尝试第二次初始化cache2k并失败

时间:2019-07-25 11:38:39

标签: spring-boot spring-test cache2k

将cache2k添加到我的项目中后,一些<properties> <java.version>1.8</java.version> <cache2k-version>1.2.2.Final</cache2k-version> </properties> <dependencies> <dependency> <groupId>org.cache2k</groupId> <artifactId>cache2k-api</artifactId> <version>${cache2k-version}</version> </dependency> <dependency> <groupId>org.cache2k</groupId> <artifactId>cache2k-core</artifactId> <version>${cache2k-version}</version> <scope>runtime</scope> </dependency> <dependency> <groupId>org.cache2k</groupId> <artifactId>cache2k-spring</artifactId> <version>${cache2k-version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> 停止工作并出现错误:

  

java.lang.IllegalStateException:已创建缓存:'cache'

下面,我提供了最小的示例来重现:

转到start.spring.io并使用Cache starter创建一个最简单的Maven项目,然后添加cache2k依赖项:

@SpringBootApplication
@EnableCaching
public class CachingDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(CachingDemoApplication.class, args);
    }

    @Bean
    public CacheManager springCacheManager() {
        SpringCache2kCacheManager cacheManager = new SpringCache2kCacheManager();
        cacheManager.addCaches(b -> b.name("cache"));
        return cacheManager;
    }

}

现在配置最简单的缓存:

@MockBean

并添加任何服务(我们将在其中的一项测试中@Service public class SomeService { public String getString() { System.out.println("Executing service method"); return "foo"; } }

@SpringBootTest

现在需要两个@SpringBootTest @RunWith(SpringRunner.class) public class SpringBootAppTest { @Test public void getString() { System.out.println("Empty test"); } } @RunWith(SpringRunner.class) @SpringBootTest public class WithMockedBeanTest { @MockBean SomeService service; @Test public void contextLoads() { } } 测试才能重现此问题:

@MockBean

请注意,第二项测试已嘲笑Caused by: java.lang.IllegalStateException: Cache already created: 'cache' at org.cache2k.core.CacheManagerImpl.newCache(CacheManagerImpl.java:174) at org.cache2k.core.InternalCache2kBuilder.buildAsIs(InternalCache2kBuilder.java:239) at org.cache2k.core.InternalCache2kBuilder.build(InternalCache2kBuilder.java:182) at org.cache2k.core.Cache2kCoreProviderImpl.createCache(Cache2kCoreProviderImpl.java:215) at org.cache2k.Cache2kBuilder.build(Cache2kBuilder.java:837) at org.cache2k.extra.spring.SpringCache2kCacheManager.buildAndWrap(SpringCache2kCacheManager.java:205) at org.cache2k.extra.spring.SpringCache2kCacheManager.lambda$addCache$2(SpringCache2kCacheManager.java:143) at java.util.concurrent.ConcurrentHashMap.compute(ConcurrentHashMap.java:1853) at org.cache2k.extra.spring.SpringCache2kCacheManager.addCache(SpringCache2kCacheManager.java:141) at org.cache2k.extra.spring.SpringCache2kCacheManager.addCaches(SpringCache2kCacheManager.java:132) at com.example.cachingdemo.CachingDemoApplication.springCacheManager(CachingDemoApplication.java:23) at com.example.cachingdemo.CachingDemoApplication$$EnhancerBySpringCGLIB$$2dce99ca.CGLIB$springCacheManager$0(<generated>) at com.example.cachingdemo.CachingDemoApplication$$EnhancerBySpringCGLIB$$2dce99ca$$FastClassBySpringCGLIB$$bbd240c0.invoke(<generated>) at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:244) at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:363) at com.example.cachingdemo.CachingDemoApplication$$EnhancerBySpringCGLIB$$2dce99ca.springCacheManager(<generated>) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154) ... 52 more 。这将导致错误(下面的stacktrace)。

@MockBean

如果您删除private messageSource: BehaviorSubject<any> = new BehaviorSubject<any>({}); message$: Observable<any> = this.messageSource.asObservable(); constructor() { let url = "/signalr"; this.connection = $.hubConnection(url); this.proxy = this.connection.createHubProxy('notificationHub'); this.proxy.on('receiveMessage', function (message) { // NgZone doesn't work here in the service this.messageSource.next(message); }); } ,则两项测试都将通过。

如何避免测试套件中出现此错误?

3 个答案:

答案 0 :(得分:1)

您的第二个测试完全代表一个不同的ApplicationContext,因此测试框架将为此启动一个专用的测试框架。如果cache2k是有状态的(例如,如果给定的类加载器已经存在,则共享CacheManager),则第二个上下文将尝试在第一个仍处于活动状态时创建新的CacheManager

您可能需要将其中一个测试标记为脏测试(请参阅@DirtiesContext),这将关闭上下文并关闭CacheManager,或者您可以使用一个不选择此选项的选项来替换缓存基础结构需要所有这些,请参见@AutoConfigureCache

如果cache2k的工作方式要求您弄脏上下文,我强烈建议您使用后面的选项交换它。

答案 1 :(得分:1)

将cache2k与Spring Dev Tools一起使用时遇到相同的错误,并以以下代码作为解决方案:

    @Bean
    public CacheManager cacheManager() {
        SpringCache2kCacheManager cacheManager = new SpringCache2kCacheManager();
        // To avoid the "Caused by: java.lang.IllegalStateException: Cache already created:"
        // error when Spring DevTools is enabled and code reloaded
        if (cacheManager.getCacheNames().stream()
            .filter(name -> name.equals("cache"))
            .count() == 0) {
            cacheManager.addCaches(
                b -> b.name("cache")
            );
        }
        return cacheManager;
    }

答案 2 :(得分:0)

由于我不需要测试中的任何自定义行为,而只是想摆脱此错误,因此解决方案是使用这样的唯一名称创建CacheManager:

@Bean
public CacheManager springCacheManager() {
    SpringCache2kCacheManager cacheManager = new SpringCache2kCacheManager("spring-" + hashCode());
    cacheManager.addCaches(b -> b.name("cache"));
    return cacheManager;
}