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

我们想测试自动配置是否正确实例化或跳过created和missing 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或跳过createOnBean或createOnMissingBean :

@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

ApplicationContextRunner如何简化自动配置测试的更多相关文章

  1. 20、Springboot 与数据访问(JDBC/自动配置)

    简介: 对于数据访问层,无论是SQL还是NOSQL,Spring Boot默认采用整合 Spring Data的方式进行统一处理,添加大量自动配置,屏蔽了很多设置.引入 各种xxxTemplate,x ...

  2. SpringBoot自动化配置之四:SpringBoot 之Starter(自动配置)、Command-line runners

    Spring Boot Starter是在SpringBoot组件中被提出来的一种概念,stackoverflow上面已经有人概括了这个starter是什么东西,想看完整的回答戳这里 Starter ...

  3. 关于SpringBoot的自动配置和启动过程

    一.简介 Spring Boot简化了Spring应用的开发,采用约定大于配置的思想,去繁从简,很方便就能构建一个独立的.产品级别的应用. 1.传统J2EE开发的缺点 开发笨重.配置繁多复杂.开发效率 ...

  4. 通用、封装、简化 webpack 配置

    通用.封装.简化 webpack 配置 现在,基本上前端的项目打包都会用上 webpack,因为 webpack 提供了无与伦比强大的功能和生态.但在创建一个项目的时候,总是免不了要配置 webpac ...

  5. Spring Boot 自动配置的原理、核心注解以及利用自动配置实现了自定义 Starter 组件

    本章内容 自定义属性快速入门 外化配置 自动配置 自定义创建 Starter 组件 摘录:读书是读完这些文字还要好好用心去想想,写书也一样,做任何事也一样 图 2 第二章目录结构图 第 2 章 Spr ...

  6. Spring Boot自动配置原理

    使用Spring Boot之后,一个整合了SpringMVC的WEB工程开发,变的无比简单,那些繁杂的配置都消失不见了,这 是如何做到的? 一切魔力的开始,都是从我们的main函数来的,所以我们再次来 ...

  7. 面试官:给我讲讲SpringBoot的依赖管理和自动配置?

    1.前言 从Spring转到SpringBoot的xdm应该都有这个感受,以前整合Spring + MyBatis + SpringMVC我们需要写一大堆的配置文件,堪称配置文件地狱,我们还要在pom ...

  8. SpringBoot入门一:基础知识(环境搭建、注解说明、创建对象方法、注入方式、集成jsp/Thymeleaf、logback日志、全局热部署、文件上传/下载、拦截器、自动配置原理等)

    SpringBoot设计目的是用来简化Spring应用的初始搭建以及开发过程.该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置.通过这种方式,SpringBoot致力于在蓬勃发 ...

  9. OEL上使用yum install oracle-validated 简化主机配置工作

    环境:OEL 5.7 + Oracle 10.2.0.5 RAC 如果你正在用OEL(Oracle Enterprise Linux)系统部署Oracle,那么可以使用yum安装oracle-vali ...

随机推荐

  1. POJ 2689.Prime Distance-区间筛素数

    最近改自己的错误代码改到要上天,心累. 这是迄今为止写的最心累的博客. Prime Distance Time Limit: 1000MS   Memory Limit: 65536K Total S ...

  2. 51nod 1101 换零钱 【完全背包变形/无限件可取】

    1101 换零钱  基准时间限制:1 秒 空间限制:131072 KB 分值: 20 难度:3级算法题  收藏  关注 N元钱换为零钱,有多少不同的换法?币值包括1 2 5分,1 2 5角,1 2 5 ...

  3. Openjudge1388 Lake Counting【DFS/Flood Fill】

    http://blog.csdn.net/c20182030/article/details/52327948 1388:Lake Counting 总时间限制:   1000ms   内存限制:  ...

  4. Bfs【p2385】 青铜莲花池

    题目描述--->p2385 青铜莲花池 分析 很明显了,题目告诉我们有八个方向,当然优先考虑bfs! 很简单的bfs,重点在于考虑清楚8个方向. 自己刚开始打错了 emmm 给大家上一个图.↓ ...

  5. (寒假集训) Cow Jog(二分优化的最长上升子数列)

    Cow Jog 时间限制: 1 Sec  内存限制: 64 MB提交: 24  解决: 5[提交][状态][讨论版] 题目描述 Farmer John's N cows (1 <= N < ...

  6. HDU 1496 Equations(哈希表)

    [题目链接] http://acm.hdu.edu.cn/showproblem.php?pid=1496 [题目大意] 给出一个方程ax1^2+bx2^2+cx3^2+dx4^2=0,求-100到1 ...

  7. linux下批量更改一个目下的目录和文件的权限

    对于网站目录我们一般设置目录的权限是755, 而文件的权限是644  ,上传目录另设 比如我们要设置web目录下的所有目录的权限是755,文件的目录是644,那么我们可以批量修改吗?答案是肯定的, 就 ...

  8. 【JPA】JPA的注解@MappedSuperclass作用解释【转】

    [代码参考地址]:GitHub地址 [原文地址][http://blog.sina.com.cn/s/blog_7085382f0100uk4p.html] [这里仅用到第三点为止] ======== ...

  9. 常用VBA小技巧

    用对话框选取文件路径(单个文件) 删除导入csv等文本文件后留下的 Data connections 增加新的工作表并并命名 Worksheets.Add(After:=Worksheets(Work ...

  10. zoj 3430 Detect the Virus(AC自己主动机)

    题目连接:zoj 3430 Detect the Virus 题目大意:给定一个编码完的串,将每个字符相应着表的数值转换成6位二进制.然后以8为一个数值,又一次形成字符 串,推断给定询问串是否含有字符 ...