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 .