1. 概览

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

在以下部分中,我们将展示ApplicationContextRunner如何简化自动配置测试。

2. 测试自动化配置方案

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

private final ApplicationContextRunner contextRunner = new ApplicationContextRunner();

让我们通过测试一些案例来展示它的魔力。

2.1. 测试Class Condition

在本节中,我们将测试一些使用@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";
}
}

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

  • ApplicationContextRunner为我们提供了withUserConfiguration方法,我们可以根据需要提供自动配置,以便为每个测试自定义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");
        });
}

通过前面的示例,我们发现测试classpath上存在某个类的场景的简单性。但是,当类不在classpath上时,我们如何测试相反的情况呢

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

@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

最新文章

  1. Hadoop 2.x
  2. Middleware In ASP.NET Core
  3. Packets(模拟 POJ1017)
  4. jQuery源码dom ready分析
  5. 调用支付宝PHP接口API实现在线即时支付功能(UTF-8编码)
  6. MySQL 5.7原生JSON格式支持
  7. smarty3--registerPlugin()函数报错问题
  8. jqueryui.position.js源代码分析
  9. Nginx实现七层负载均衡配置指导
  10. ZJOI2017 Day3 滚粗记
  11. datatable 多字段 排序;
  12. jdk 动态代理源码分析
  13. C语言实型常量
  14. 树的子结构(JAVA)
  15. Delphi XE4 For IOS之部署问题
  16. leetcode39
  17. [UE4]Axis Mappings轴映射和动作映射Action Mappings的区别
  18. Leetcode题库——17.电话号码的字母组合
  19. Linux 系统文件类型
  20. Linux 安装 MantisBT 详解

热门文章

  1. 牛客网 暑期ACM多校训练营(第二场)J.farm-STL(vector)+二维树状数组区间更新、单点查询 or 大暴力?
  2. Codeforces Gym100952 D. Time to go back-杨辉三角处理组合数 (2015 HIAST Collegiate Programming Contest)
  3. [Bzoj5285][洛谷P4424][HNOI/AHOI2018]寻宝游戏(bitset)
  4. DB Link
  5. VUE -- 不推荐使用jQuery
  6. UVa11988 Broken Keyboard(练习链表使用)
  7. DBS:同学录
  8. thrift.transport.TTransport.TTransportException: Could not start SASL: Error in sasl_client_start (-4) SASL(-4): no mechanism available: No worthy mechs found
  9. 栅格 CSS中的循环 媒体查询
  10. 前端存储之Web Sql Database