Создайте экземпляр другого менеджера кеша в каждом тестовом классе

В моем проекте веб-приложения Spring-Boot я использую Spring Cache для реализации кэширования. Кэш можно включить/отключить с помощью ключа конфигурации, определенного в application.yml. У меня уже есть тестовые примеры, в которых тесты написаны при условии отсутствия кеша. Итак, по умолчанию в моем профиле integration-test кеширование отключено, и я инициализирую NoOpCacheManager, и все мои тесты работают.

@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;
        }
    }

}

Я также хочу написать тесты для проверки функциональности кэширования. Я создал класс CachedControllerTest, в котором написаны все тесты, специфичные для кеша.

Проблема в том, что когда я бегу

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

Все тесты в классе CachedControllerTest терпят неудачу, потому что диспетчер кеша инициализируется с NoOpCacheManager, хотя я включил кеширование в функции bean.

Я попытался создать отдельный профиль для CachedControllerTest, и он все еще терпит неудачу, потому что после инициализации bean-компонента cacheManager он не сбрасывается.

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

Вот мой класс CachedControllerTest

@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;
        }
    }
}

приложение.yml

spring:
    profiles: integration-test

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

cache:
    enabled: false 

Мое требование состоит в том, чтобы повторно инициализировать cacheManage для каждого тестового класса, а CacheConfig — это bean-компонент, который я хочу изменить во время выполнения, чтобы можно было инициализировать соответствующий CacheManager.

В изоляции, если я запускаю тесты класса CachedControllerTest, все они проходят, потому что нет другого тестового класса, запускаемого до того, который инициализировал бы cacheManager до NoOpCacheManager.

Заранее спасибо за любую помощь/предложение, чтобы эта ситуация работала.

Изменить 1

По предложению Сэма добавлено @ActiveProfiles.

Изменить 2

Определение класса AbstractRestControllerTest

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

person Nitin Arora    schedule 25.04.2015    source источник
comment
К вашему сведению: AFTER_CLASS по умолчанию ClassMode. Таким образом, нет причин переопределять значение по умолчанию значением по умолчанию. Другими словами, будет достаточно пустого объявления @DirtiesContext на уровне класса.   -  person Sam Brannen    schedule 26.04.2015
comment
Спасибо за совет @SamBrannen.   -  person Nitin Arora    schedule 27.04.2015
comment
Вы используете StaticContextHolder? Если да, то как он определяется как Spring bean-компонент через сканирование компонентов?   -  person Sam Brannen    schedule 27.04.2015


Ответы (1)


@Profile оказывает нулевое влияние на тестовый класс.

Чтобы установить профили определения активных компонентов для интеграционного теста, необходимо использовать @ActiveProfiles из модуля spring-test.

Обратитесь к Конфигурация контекста с профилями среды Справочного руководства Spring.

Кроме того, CachedControllerTestsBeanConfig должен быть снабжен аннотацией @Configuration не @Component.

С уважением,

Сэм (автор Spring TestContext Framework)

person Sam Brannen    schedule 26.04.2015
comment
Добавлен @ActiveProfiles("integration-cache-test") в класс CachedControllerTest. Но последующие тестовые классы используют диспетчер кеша, инициализированный классом CachedControllersTest. DirtiesContext добавляется только @CachedControllersTest. Я думал, что другие тестовые классы, которые выполняют тесты после CachedControllersTest, будут использовать NoOpCacheManager. Но они терпят неудачу, потому что данные обслуживаются из кеша, и все утверждения терпят неудачу. - person Nitin Arora; 27.04.2015
comment
Как настроен AbstractRestControllerTest (и любой из его суперклассов)? Он использует @ContextConfiguration? Использует ли он @SpringApplicationConfiguration Spring Boot? и т.п. - person Sam Brannen; 27.04.2015
comment
Как CacheBeanTestsConfig подбирается (т. е. объявляется) в качестве класса конфигурации в тестах? - person Sam Brannen; 27.04.2015
comment
Как CachedControllerTestsBeanConfig используется в качестве класса конфигурации? - person Sam Brannen; 27.04.2015
comment
Если вы пропустили мое обновление: CachedControllerTestsBeanConfig должно быть аннотировано @Configuration, а не @Component. - person Sam Brannen; 27.04.2015
comment
1. Да AbstractRestControllerTest использует @SpringApplicationConfiguration 2. CacheBeanTestsConfig был точной копией CacheBeanConfig в исходном коде. Я добавил @Profile на CacheBeanConfig. Обновил код в посте. Да, это украшено @Configuration 3. CachedControllerTestsBeanConfig украшено @Configutation - person Nitin Arora; 27.04.2015
comment
Вот суть журналов частичного отказа. gist.github.com/627e49fd2b1fe999270a.git . Последовательность выполнения тестовых классов: 1. Запуск com.declara.server.annotations.ControllerAnnotationTest 2. Запуск com.declara.server.cache.CachedControllersTest 3. Другие тестовые классы - person Nitin Arora; 27.04.2015
comment
Если я добавлю @DirtiesContext к ControllerAnnotationTest первому тестовому классу в последовательности выполнения теста. Тогда все тесты проходят. Но тогда вы никогда не знаете, какой из них будет первым тестовым классом в другой среде. Это потребует помещения @DirtiesContext в AbstractControllerTest, чего я хочу избежать, потому что это замедлит выполнение тестовых случаев. - person Nitin Arora; 27.04.2015