Рубрики
Без рубрики

Руководство по ApplicationContext Runner в Spring Boot

Узнайте, как сделать все возможное из ApplicationContext Runner в Spring Boot.

Автор оригинала: baeldung.

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 .