Перезагрузить или обновить контекст приложения Spring внутри тестового метода?

Мне нужно изменить профили Spring, которые активны в моем applicationContext, в одном методе моего тестового класса, и для этого мне нужно запустить одну строку кода перед обновлением конкурса, потому что я использую ProfileResolver. Я пробовал следующее:

@WebAppConfiguration
@ContextConfiguration(locations = {"/web/WEB-INF/spring.xml"})
@ActiveProfiles(resolver = BaseActiveProfilesResolverTest.class)
public class ControllerTest extends AbstractTestNGSpringContextTests {
    @Test
    public void test() throws Exception {
        codeToSetActiveProfiles(...);
        ((ConfigurableApplicationContext)this.applicationContext).refresh();
        ... tests here ...
        codeToSetActiveProfiles(... back to prior profiles ...);
        ... ideally refresh/reload the context for future tests
    }
}

Но я получаю:

java.lang.IllegalStateException: GenericApplicationContext does not support multiple refresh attempts: just call 'refresh' once

DirtiesContext не работает для меня, потому что он запускается ПОСЛЕ выполнения класса / метода, а не раньше, и мне все равно нужно выполнить строку кода перед запуском обновления / перезагрузки.

Какие-либо предложения? Я попытался просмотреть запущенные прослушиватели / хуки, но не увидел очевидного места, куда можно было бы вставить себя, чтобы добиться такого поведения.


person David E    schedule 13.07.2014    source источник


Ответы (3)


По замыслу, программное обновление ApplicationContext явно не поддерживается Spring TestContext Framework. Кроме того, не предполагается, что метод тестирования обновляет контекст.

Таким образом, я бы рекомендовал вам переоценить свою потребность в обновлении и рассмотреть альтернативы, такие как размещение методов тестирования, для которых требуется другой набор активных профилей, в выделенном тестовом классе.

Таким образом, @ActiveProfiles поддерживает декларативную конфигурацию (через атрибуты value и profiles) и программную конфигурацию (через атрибут resolver) активных профилей для тестов, но только в тестовом классе. уровень (не на уровне метода). Другой вариант - реализовать ApplicationContextInitializer и настроить его через @ContextConfiguration(initializers=...).

Единственный другой способ повлиять на ApplicationContext перед его обновлением - это реализовать SmartContextLoader или расширить один из предоставленных классов и настроить его через @ContextConfiguration(loader=...). Например, AbstractGenericContextLoader.customizeContext() позволяет «настроить GenericApplicationContext, созданный загрузчиком после загрузки определений bean-компонентов в контекст, но до обновления контекста».

С наилучшими пожеланиями,

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

person Sam Brannen    schedule 13.07.2014
comment
Знаете ли вы какой-либо пример реализации SmartContextLoader (кроме предоставленных классов)? Потому что я пытался расширить эти классы, и это оказалось слишком сложно. Я перестал использовать @Configuration, когда мне нужны другие ApplicationContext классы, кроме Generic. Но я также знаю, что гораздо удобнее использовать инструменты тестирования Spring ... - person Serge Ballesta; 13.07.2014
comment
Спасибо за ответ, Сэм, здорово, что у меня есть эксперт в этом вопросе. Я просмотрел то, что поставляется с AbstractGenericWebContextLoader.customizeContext, это GenericWebApplicationContext и WebMergedContextConfiguration. Я не видел никаких методов для настройки профилей, вы можете мне указать? - person David E; 14.07.2014
comment
На GenericWebApplicationContext вы можете вызывать context.getEnvironment().setActiveProfiles(...) и т. Д. - person Sam Brannen; 14.07.2014
comment
Что касается примеров SmartContextLoader реализаций, вы можете использовать Google для реализации SmartContextLoader (включая кавычки), но это не даст много результатов, поскольку большинство людей либо расширяют AbstractContextLoader, либо AbstractGenericContextLoader. Например, Spring Boot представил свой собственный SpringApplicationContextLoader, который напрямую расширяет AbstractContextLoader. - person Sam Brannen; 14.07.2014
comment
Спасибо, Сэм, я ценю отличные ответы и отличный фреймворк. Сегодня мы просто внутренне болтали о том, насколько болезненно было бы проводить тестирование до этого. Спасибо за ваш тяжелый труд! - person David E; 18.07.2014
comment
Добро пожаловать, Дэвид. И спасибо за похвалу! Я очень ценю это. Удачного кодирования :) - person Sam Brannen; 20.07.2014

Есть неплохой небольшой прием для запуска обновления контекста - использовать org.springframework.cloud.context.refresh.ContextRefresher.

Я не уверен на 100%, что этот метод подойдет вам: он требует spring-cloud-context зависимости. Однако это может быть добавлено просто как test зависимость и не попадет в рабочий путь к классам.

Чтобы воспользоваться этим обновлением, вам также необходимо импортировать org.springframework.cloud.autoconfigure.RefreshAutoConfiguration конфигурацию, которая добавляет область RefreshScope к вашему applicationContext, который фактически выполняет свою работу под капотом.

Итак, измените тест следующим образом:

import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.autoconfigure.RefreshAutoConfiguration;
import org.springframework.cloud.context.refresh.ContextRefresher;    
// your other imports


@WebAppConfiguration
@ContextConfiguration(locations = {"/web/WEB-INF/spring.xml"}, classes = RefreshAutoConfiguration.class)
@ActiveProfiles(resolver = BaseActiveProfilesResolverTest.class)
public class ControllerTest extends AbstractTestNGSpringContextTests {

    @Autowired
    private ContextRefresher contextRefresher;

    @Test
    public void test() throws Exception {
        // doSmth before
        contextRefresher.refresh();
        // context is refreshed - continue testing
    }

}
person Ivan Pronin    schedule 19.03.2019

Не все контексты приложения поддерживают несколько refresh. Согласно javadoc для AbstractRefreshableApplicationContext только его подклассы или AbstractRefreshableWebApplicationContext принимают refresh более одного раза ... и GenericApplicationContext ни в одном из них.

Вы должны использовать другой класс для вашего ApplicationContext для поддержки горячего обновления.

Редактировать :

Поскольку вы используете аннотацию @ContextConfiguration, вам следует использовать пользовательскую реализацию ContextLoader или SmartContextLoader, чтобы заставить spring использовать менее глупый ApplicationContext. Но я так и не нашел чистого и аккуратного пути к этому. Поэтому, когда мне нужен XmlWebApplicationContext в моих тестовых классах, я не использую @ContextConfiguration, а создаю и обновляю свой контекст вручную в методе @Before или в начале теста.

Я понимаю, что это не совсем ответ на ваш вопрос, но вы можете рассматривать это как обходной путь.

person Serge Ballesta    schedule 13.07.2014
comment
Согласен, хотя я не вижу, как повлиять на выбор создаваемого класса контекста приложения. По умолчанию тестовая инфраструктура Spring создает GenericWebApplicationContext (подкласс GenericApplicationContext, показанный выше в исключении). - person David E; 13.07.2014
comment
Упс, не заметил, что вы используете @ContextConfiguration ... Я только что обновил свой пост. - person Serge Ballesta; 13.07.2014