ApplicationContextRunner如何简化自动配置测试

 

1. 概览

众所周知,自动配置是Spring Boot的关键功能之一, 但测试自动配置可能会很棘手。git

在如下部分中,咱们将展现ApplicationContextRunner如何简化自动配置测试。github

2. 测试自动化配置方案

ApplicationContextRunner是一个实用程序类,它运行ApplicationContext并提供AssertJ样式断言。 最好用做测试类中的字段以便共享配置,而后咱们在每一个测试中进行自定义:web

private final ApplicationContextRunner contextRunner = new ApplicationContextRunner();

让咱们经过测试一些案例来展现它的魔力。spring

2.1. 测试Class Condition

在本节中,咱们将测试一些使用@ConditionalOnClass和@ConditionalOnMissingClass 注解的自动配置类:bash

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

咱们想测试自动配置是否正确实例化或跳过createdmissing beans给定的预期条件。spring-boot

  • ApplicationContextRunner为咱们提供了withUserConfiguration方法,咱们能够根据须要提供自动配置,以便为每一个测试自定义ApplicationContext测试

  • run 方法将 ContextConsumer 做为将断言应用于上下文的参数。 测试退出时,ApplicationContext将自动关闭:ui

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

经过前面的示例,咱们发现测试classpath上存在某个类的场景的简单性。可是,当类不在classpath上时,咱们如何测试相反的状况呢this

这就是FilteredClassLoader发挥做用的地方。它用于在运行时过滤classpath上指定的类:spa

@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. 测试 Bean Condition

咱们刚刚测试了 @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方法,而后发送咱们的自定义配置类来测试自动配置是否在不一样的条件下恰当地实例化bean或跳过createOnBeancreateOnMissingBean :

@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. 测试 Property Condition

在本节中,咱们测试使用 @ConditionalOnPropertyannotations的自动配置类。

首先,咱们须要这个测试的属性:

com.baeldung.service=custom

而后,咱们编写嵌套的自动配置类,根据前面的属性建立bean:

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

如今,咱们调用withPropertyValues方法来覆盖每一个测试中的属性值:

@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. 结论

总结一下, 这篇教程主要展现 如何使用ApplicationContextRunner运行带有自定义的ApplicationContext并应用断言.

咱们在这里介绍了最经常使用的场景,而不是列出如何自定义ApplicationContext 。

在此期间,请记住ApplicationConetxtRunner适用于非Web应用程序,所以请考虑WebApplicationContextRunner用于基于servlet的Web应用程序,ReactiveWebApplicationContextRunner用于响应式Web应用程序。

本文源代码,请访问GitHub

原文:www.baeldung.com/spring-boot…

做者:baeldung

译者:Leesen

 

相关文章
相关标签/搜索