Nitin Arora
Nitin Arora

Reputation: 2668

Instantiate different cache manager in each Test Class

In my Spring-Boot Web Application project I'm using Spring Cache to implement caching. Cache can be enabled/disabled by configuration key defined in application.yml. I already have existing test cases where tests are written assuming there is no cache. So by default in my integration-test profile caching is disabled and I initialize NoOpCacheManager and all my tests work.

@Profile(value = { "default", "production", "integration-test" })
@Configuration
@EnableCaching(mode = AdviceMode.ASPECTJ)
public class CacheBeanConfig extends CachingConfigurerSupport {

    @Autowired
    private CacheConfig cacheConfig;

    @Bean
    @Override
    public CacheManager cacheManager() {
        if (cacheConfig.isEnabled()) {
            System.out.println("****************Couchbase CacheBeanTestsConfig cache init.**********************");
            Map<String, DeclaraCouchbaseTemplate> cacheCouchTemplateMap = Maps.newHashMap();
            Map<String, Integer> cacheTtlMap = Maps.newHashMap();

            for (CacheConfig.CacheConfigParam cacheParam : cacheConfig.getCaches()) {
                try {
                    cacheCouchTemplateMap.put(cacheParam.getName(),
                        couchbaseManager.getCouchbaseTemplate(cacheParam.getName()));
                    cacheTtlMap.put(cacheParam.getName(), cacheParam.getTtl());
                } catch (IOException | URISyntaxException e) {
                    throw new FaultException("Unable to get couchbase template.");
                }
            }

            return new CouchbaseCacheManager(cacheCouchTemplateMap, cacheTtlMap, metricRegistry);
        } else {
            System.out.println("****************NoOp CacheBeanTestsConfig cache init.**********************");
            NoOpCacheManager noopCacheManager = new NoOpCacheManager();
            return noopCacheManager;
        }
    }

}

I also want to write tests for verification of Caching functionality. I created a CachedControllerTest class where all the cache specific tests are written.

Problem is when I run

mvn test -Dtest=CachedControllersTest -Dspring.profiles.active=integration-test

All the tests in CachedControllerTest class are failing because cache manager is initialized with NoOpCacheManager even though I enabled the caching in the bean function.

I tried to create a separate profile for CachedControllerTest and it still fails because once cacheManager bean is initialized it is not getting reset.

mvn test -Dtest=CachedControllersTest -Dspring.profiles.active=integration-test,integration-cache-test

Here is my CachedControllerTest class

@ActiveProfiles("integration-cache-test")
@DirtiesContext
public class CachedControllersTest extends AbstractRestControllerTest {

    @Configuration
    @EnableCaching(mode = AdviceMode.ASPECTJ)
    @Profile("integration-cache-test")
    public static class CachedControllerTestsBeanConfig {

        @Autowired
        private CouchbaseManager couchbaseManager;

        @Autowired
        private CacheConfig cacheConfig;

        @Autowired
        private MetricRegistry metricRegistry;

        @Autowired
        GlobalApplicationConfig globalAppConfig;

        @Bean
        public CacheManager cacheManager() {
            System.out.println("**************** CachedControllerTestsBeanConfig EnabledCaching**********************");
            cacheConfig.setEnabled(true);
            if (cacheConfig.isEnabled()) {
                System.out.println("****************Couchbase CachedControllerTestsBeanConfig cache init.**********************");
                Map<String, DeclaraCouchbaseTemplate> cacheCouchTemplateMap = Maps.newHashMap();
                Map<String, Integer> cacheTtlMap = Maps.newHashMap();

                for (CacheConfig.CacheConfigParam cacheParam : cacheConfig.getCaches()) {
                    try {
                        cacheCouchTemplateMap.put(cacheParam.getName(),
                            couchbaseManager.getCouchbaseTemplate(cacheParam.getName()));
                        cacheTtlMap.put(cacheParam.getName(), cacheParam.getTtl());
                    } catch (IOException | URISyntaxException e) {
                        throw new FaultException("Unable to get couchbase template.");
                    }
                }

                return new CouchbaseCacheManager(cacheCouchTemplateMap, cacheTtlMap, metricRegistry);
            } else {
                System.out.println("****************NoOp CachedControllerTestsBeanConfig cache init.**********************");
                NoOpCacheManager noopCacheManager = new NoOpCacheManager();
                return noopCacheManager;
            }
        }

        @Bean(name = "mtlKeyGenerator")
        public KeyGenerator keyGenerator() {
            System.out.println("****************CachedControllerTestsBeanConfig mtlKeyGenerator.**********************");
            return new MultiTenantKeyGenerator(globalAppConfig.getTenantId());
        }

        @Bean(name = CacheManagementConfigUtils.CACHE_ASPECT_BEAN_NAME)
        @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
        public AnnotationGroupCacheAspect cacheAspect() {
            AnnotationGroupCacheAspect cacheAspect = AnnotationGroupCacheAspect.aspectOf();
            CacheManager cacheManager = (CacheManager) StaticContextHolder.getApplicationContext().getBean("cacheManager");
            cacheAspect.setCacheManager(cacheManager);

            KeyGenerator keyGenerator = (KeyGenerator) StaticContextHolder.getApplicationContext().getBean("mtlKeyGenerator");
            cacheAspect.setKeyGenerator(keyGenerator);
            return cacheAspect;
        }
    }

    @Component
    public static class StaticContextHolder implements ApplicationContextAware {

        private static ApplicationContext appContext;

        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            appContext = applicationContext;
        }

        public static ApplicationContext getApplicationContext() {
            return appContext;
        }
    }
}

application.yml

spring:
    profiles: integration-test

cache:
    enabled: false
---
spring:
    profiles: integration-cache-test

cache:
    enabled: false 

My requirement is to reinitialize the cacheManage for each Test Class and CacheConfig is the bean which I want to modify at the runtime so that appropriate CacheManager can be initialized.

In isolation if I run the CachedControllerTest class tests they all pass because there is no other Test Class run before that which would have initialized the cacheManager to NoOpCacheManager.

Thanks in advance for any help/suggestion to make this situation work.

Edit 1

Based on the suggestion by Sam, Added @ActiveProfiles.

Edit 2

AbstractRestControllerTest Class Definition

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
@WebAppConfiguration
public class AbstractRestControllerTest {
}

Upvotes: 1

Views: 3106

Answers (1)

Sam Brannen
Sam Brannen

Reputation: 31177

@Profile has zero effect on a test class.

To set the active bean definition profiles for an integration test, you need to use @ActiveProfiles from the spring-testmodule.

Consult the Context configuration with environment profiles section of the Spring Reference Manual for details.

Also, CachedControllerTestsBeanConfig must be annotated with @Configuration not @Component.

Regards,

Sam (author of the Spring TestContext Framework)

Upvotes: 1

Related Questions