1. Обзор
Хорошо известно , что автоматическая настройка является одной из ключевых функций Spring Boot, но тестирование сценариев автоматической настройки может быть сложным.
В следующих разделах мы покажем, как | ApplicationContext Runner упрощает тестирование автоматической конфигурации.
2. Тестовые Сценарии Автоматической Настройки
ApplicationContext Runner – это служебный класс, который запускает ApplicationContext и предоставляет утверждения стиля AssertJ . Это лучше всего использовать в качестве поля в тестовом классе для общей конфигурации, и мы делаем настройки в каждом тесте после этого:
private final ApplicationContextRunner contextRunner
= new ApplicationContextRunner();
Давайте перейдем к демонстрации его магии, протестировав несколько случаев.
2.1. Состояние Испытательного класса
В этом разделе мы собираемся протестировать некоторые классы автоматической конфигурации, которые используют @ConditionalOnClass и @ConditionalOnMissingClass аннотации :
@Configuration
@ConditionalOnClass(ConditionalOnClassIntegrationTest.class)
protected static class ConditionalOnClassConfiguration {
@Bean
public String created() {
return "This is created when ConditionalOnClassIntegrationTest "
+ "is present on the classpath";
}
}
@Configuration
@ConditionalOnMissingClass(
"com.baeldung.autoconfiguration.ConditionalOnClassIntegrationTest"
)
protected static class ConditionalOnMissingClassConfiguration {
@Bean
public String missed() {
return "This is missed when ConditionalOnClassIntegrationTest "
+ "is present on the classpath";
}
}Мы хотели бы проверить, правильно ли автоконфигурация создает или пропускает созданные и пропущенные бобы при заданных ожидаемых условиях.
ApplicationContext Runner дает нам метод with User Configuration , где мы можем предоставить автоматическую конфигурацию по требованию для настройки ApplicationContext для каждого теста.
Метод run принимает ContextConsumer в качестве параметра, который применяет утверждения к контексту. ApplicationContext будет автоматически закрыт при выходе из теста:
@Test
public void whenDependentClassIsPresent_thenBeanCreated() {
this.contextRunner.withUserConfiguration(ConditionalOnClassConfiguration.class)
.run(context -> {
assertThat(context).hasBean("created");
assertThat(context.getBean("created"))
.isEqualTo("This is created when ConditionalOnClassIntegrationTest "
+ "is present on the classpath");
});
}
@Test
public void whenDependentClassIsPresent_thenBeanMissing() {
this.contextRunner.withUserConfiguration(ConditionalOnMissingClassConfiguration.class)
.run(context -> {
assertThat(context).doesNotHaveBean("missed");
});
}
В предыдущем примере мы видим простоту тестирования сценариев, в которых определенный класс присутствует на пути к классу. Но как мы собираемся проверить обратное, когда класс отсутствует на пути к классу?
Вот тут-то и срабатывает Filtered ClassLoader . Он используется для фильтрации указанных классов по пути к классам во время выполнения:
@Test
public void whenDependentClassIsNotPresent_thenBeanMissing() {
this.contextRunner.withUserConfiguration(ConditionalOnClassConfiguration.class)
.withClassLoader(new FilteredClassLoader(ConditionalOnClassIntegrationTest.class))
.run((context) -> {
assertThat(context).doesNotHaveBean("created");
assertThat(context).doesNotHaveBean(ConditionalOnClassIntegrationTest.class);
});
}
@Test
public void whenDependentClassIsNotPresent_thenBeanCreated() {
this.contextRunner.withUserConfiguration(ConditionalOnMissingClassConfiguration.class)
.withClassLoader(new FilteredClassLoader(ConditionalOnClassIntegrationTest.class))
.run((context) -> {
assertThat(context).hasBean("missed");
assertThat(context).getBean("missed")
.isEqualTo("This is missed when ConditionalOnClassIntegrationTest "
+ "is present on the classpath");
assertThat(context).doesNotHaveBean(ConditionalOnClassIntegrationTest.class);
});
}2.2. Тестовое состояние Бобов
Мы только что рассмотрели тестирование @ConditionalOnClass и @ConditionalOnMissingClass аннотаций, а теперь давайте посмотрим, как выглядят вещи, когда мы используем @ConditionalOnBean и @ConditionalOnMissingBean аннотации.
Чтобы начать, нам точно так же нужно несколько классов автоматической конфигурации :
@Configuration
protected static class BasicConfiguration {
@Bean
public String created() {
return "This is always created";
}
}
@Configuration
@ConditionalOnBean(name = "created")
protected static class ConditionalOnBeanConfiguration {
@Bean
public String createOnBean() {
return "This is created when bean (name=created) is present";
}
}
@Configuration
@ConditionalOnMissingBean(name = "created")
protected static class ConditionalOnMissingBeanConfiguration {
@Bean
public String createOnMissingBean() {
return "This is created when bean (name=created) is missing";
}
}
Затем мы вызовем метод withUserConfiguration , как в предыдущем разделе, и отправим наш пользовательский класс конфигурации, чтобы проверить, правильно ли автоконфигурация создает экземпляр или пропускает createOnBean или createOnMissingBean beans в разных условиях :
@Test
public void whenDependentBeanIsPresent_thenConditionalBeanCreated() {
this.contextRunner.withUserConfiguration(
BasicConfiguration.class,
ConditionalOnBeanConfiguration.class
)
// ommitted for brevity
}
@Test
public void whenDependentBeanIsNotPresent_thenConditionalMissingBeanCreated() {
this.contextRunner.withUserConfiguration(ConditionalOnMissingBeanConfiguration.class)
// ommitted for brevity
}2.3. Состояние Тестового Свойства
В этом разделе давайте протестируем классы автоматической конфигурации, которые используют @ConditionalOnProperty аннотации .
Во-первых, нам нужно свойство для этого теста:
com.baeldung.service=custom
После этого мы пишем вложенные классы автоматической конфигурации для создания бобов на основе предыдущего свойства:
@Configuration
@TestPropertySource("classpath:ConditionalOnPropertyTest.properties")
protected static class SimpleServiceConfiguration {
@Bean
@ConditionalOnProperty(name = "com.baeldung.service", havingValue = "default")
@ConditionalOnMissingBean
public DefaultService defaultService() {
return new DefaultService();
} @Bean
@ConditionalOnProperty(name = "com.baeldung.service", havingValue = "custom")
@ConditionalOnMissingBean
public CustomService customService() {
return new CustomService();
}
}
Теперь мы вызываем метод with Property Values для переопределения значения свойства в каждом тесте:
@Test
public void whenGivenCustomPropertyValue_thenCustomServiceCreated() {
this.contextRunner.withPropertyValues("com.baeldung.service=custom")
.withUserConfiguration(SimpleServiceConfiguration.class)
.run(context -> {
assertThat(context).hasBean("customService");
SimpleService simpleService = context.getBean(CustomService.class);
assertThat(simpleService.serve()).isEqualTo("Custom Service");
assertThat(context).doesNotHaveBean("defaultService");
});
}
@Test
public void whenGivenDefaultPropertyValue_thenDefaultServiceCreated() {
this.contextRunner.withPropertyValues("com.baeldung.service=default")
.withUserConfiguration(SimpleServiceConfiguration.class)
.run(context -> {
assertThat(context).hasBean("defaultService");
SimpleService simpleService = context.getBean(DefaultService.class);
assertThat(simpleService.serve()).isEqualTo("Default Service");
assertThat(context).doesNotHaveBean("customService");
});
}
3. Заключение
Подводя итог, этот учебник просто показал как использовать ApplicationContext Runner для запуска ApplicationContext с настройками и применением утверждений .
Мы рассмотрели здесь наиболее часто используемые сценарии вместо исчерпывающего списка того, как настроить ApplicationContext.
В то же время, пожалуйста, имейте в виду, что ApplicationContext Runner предназначен для не веб-приложений, поэтому рассмотрите WebApplicationContextRunner для веб-приложений на основе сервлетов и ReactiveWebApplicationContextRunner для реактивных веб-приложений.
Исходный код этого учебника можно найти на GitHub .