Github地址

在Spring引入Java Config机制之后,我们会越来越多的使用@Configuration来注册Bean,并且Spring Boot更广泛地使用了这一机制,其提供的大量Auto Configuration大大简化了配置工作。那么问题来了,如何确保@Configuration和Auto Configuration按照预期运行呢,是否正确地注册了Bean呢?本章举例测试@Configuration和Auto Configuration的方法(因为Auto Configuration也是@Configuration,所以测试方法是一样的)。

例子1:测试@Configuration

我们先写一个简单的@Configuration:

@Configuration
public class FooConfiguration {@Beanpublic Foo foo() {return new Foo();}}

然后看FooConfiguration是否能够正确地注册Bean:

public class FooConfigurationTest {private AnnotationConfigApplicationContext context;@BeforeMethodpublic void init() {context = new AnnotationConfigApplicationContext();}@AfterMethod(alwaysRun = true)public void reset() {context.close();}@Testpublic void testFooCreation() {context.register(FooConfiguration.class);context.refresh();assertNotNull(context.getBean(Foo.class));}}

注意上面代码中关于Context的代码:

  1. 首先,我们构造一个Context

  2. 然后,注册FooConfiguration

  3. 然后,refresh Context

  4. 最后,在测试方法结尾close Context

如果你看Spring Boot中关于@Configuration测试的源代码会发现和上面的代码有点不一样:

public class DataSourceAutoConfigurationTests {private final AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();@Beforepublic void init() {EmbeddedDatabaseConnection.override = null;EnvironmentTestUtils.addEnvironment(this.context,"spring.datasource.initialize:false","spring.datasource.url:jdbc:hsqldb:mem:testdb-" + new Random().nextInt());}@Afterpublic void restore() {EmbeddedDatabaseConnection.override = null;this.context.close();}

这是因为Spring和Spring Boot都是用JUnit做测试的,而JUnit的特性是每次执行测试方法前,都会new一个测试类实例,而TestNG是在共享同一个测试类实例的。

例子2:测试@Conditional

Spring Framework提供了一种可以条件控制@Configuration的机制,即只在满足某条件的情况下才会导入@Configuration,这就是@Conditional。

下面我们来对@Conditional做一些测试,首先我们自定义一个Condition FooConfiguration:

@Configuration
public class FooConfiguration {@Bean@Conditional(FooCondition.class)public Foo foo() {return new Foo();}public static class FooCondition implements Condition {@Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {if (context.getEnvironment() != null) {Boolean property = context.getEnvironment().getProperty("foo.create", Boolean.class);return Boolean.TRUE.equals(property);}return false;}}
}

该Condition判断Environment中是否有foo.create=true

如果我们要测试这个Condition,那么就必须往Environment里添加相关property才可以,在这里我们测试了三种情况:

  1. 没有配置foo.create=true

  2. 配置foo.create=true

  3. 配置foo.create=false

FooConfigurationTest:

public class FooConfigurationTest {private AnnotationConfigApplicationContext context;@BeforeMethodpublic void init() {context = new AnnotationConfigApplicationContext();}@AfterMethod(alwaysRun = true)public void reset() {context.close();}@Test(expectedExceptions = NoSuchBeanDefinitionException.class)public void testFooCreatePropertyNull() {context.register(FooConfiguration.class);context.refresh();context.getBean(Foo.class);}@Testpublic void testFooCreatePropertyTrue() {context.getEnvironment().getPropertySources().addLast(new MapPropertySource("test", Collections.singletonMap("foo.create", "true")));context.register(FooConfiguration.class);context.refresh();assertNotNull(context.getBean(Foo.class));}@Test(expectedExceptions = NoSuchBeanDefinitionException.class)public void testFooCreatePropertyFalse() {context.getEnvironment().getPropertySources().addLast(new MapPropertySource("test", Collections.singletonMap("foo.create", "false")));context.register(FooConfiguration.class);context.refresh();assertNotNull(context.getBean(Foo.class));}}

注意我们用以下方法来给Environment添加property:

context.getEnvironment().getPropertySources().addLast(new MapPropertySource("test", Collections.singletonMap("foo.create", "true"))
);

所以针对@Conditional和其对应的Condition的测试的根本就是给它不一样的条件,判断其行为是否正确,在这个例子里我们的Condition比较简单,只是判断是否存在某个property,如果复杂Condition的话,测试思路也是一样的。

例子3:测试@ConditionalOnProperty

Spring framework只提供了@Conditional,Spring boot对这个机制做了扩展,提供了更为丰富的@ConditionalOn*,这里我们以@ConditionalOnProperty举例说明。

先看FooConfiguration:

@Configuration
public class FooConfiguration {@Bean@ConditionalOnProperty(prefix = "foo", name = "create", havingValue = "true")public Foo foo() {return new Foo();}}

FooConfigurationTest:

public class FooConfigurationTest {private AnnotationConfigApplicationContext context;@BeforeMethodpublic void init() {context = new AnnotationConfigApplicationContext();}@AfterMethod(alwaysRun = true)public void reset() {context.close();}@Test(expectedExceptions = NoSuchBeanDefinitionException.class)public void testFooCreatePropertyNull() {context.register(FooConfiguration.class);context.refresh();context.getBean(Foo.class);}@Testpublic void testFooCreatePropertyTrue() {EnvironmentTestUtils.addEnvironment(context, "foo.create=true");context.register(FooConfiguration.class);context.refresh();assertNotNull(context.getBean(Foo.class));}@Test(expectedExceptions = NoSuchBeanDefinitionException.class)public void testFooCreatePropertyFalse() {EnvironmentTestUtils.addEnvironment(context, "foo.create=false");context.register(FooConfiguration.class);context.refresh();assertNotNull(context.getBean(Foo.class));}}

这段测试代码和例子2的逻辑差不多,只不过例子2里使用了我们自己写的Condition,这里使用了Spring Boot提供的@ConditionalOnProperty。

并且利用了Spring Boot提供的EnvironmentTestUtils简化了给Environment添加property的工作:

EnvironmentTestUtils.addEnvironment(context, "foo.create=false");

例子4:测试Configuration Properties

Spring Boot还提供了类型安全的Configuration Properties,下面举例如何对其进行测试。

BarConfiguration:

@Configuration
@EnableConfigurationProperties(BarConfiguration.BarProperties.class)
public class BarConfiguration {@Autowiredprivate BarProperties barProperties;@Beanpublic Bar bar() {return new Bar(barProperties.getName());}@ConfigurationProperties("bar")public static class BarProperties {private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}}}

BarConfigurationTest:

public class BarConfigurationTest {private AnnotationConfigApplicationContext context;@BeforeMethodpublic void init() {context = new AnnotationConfigApplicationContext();}@AfterMethod(alwaysRun = true)public void reset() {context.close();}@Testpublic void testBarCreation() {EnvironmentTestUtils.addEnvironment(context, "bar.name=test");context.register(BarConfiguration.class, PropertyPlaceholderAutoConfiguration.class);context.refresh();assertEquals(context.getBean(Bar.class).getName(), "test");}}

注意到因为我们使用了Configuration Properties机制,需要注册PropertyPlaceholderAutoConfiguration,否则在BarConfiguration里无法注入BarProperties。

参考文档

  • Conditionally include @Configuration classes or @Bean methods

  • Condition annotations

  • Type-safe Configuration Properties

  • Spring Framework Testing

  • Spring Boot Testing

Spring、Spring Boot和TestNG测试指南 - 测试@Configuration相关推荐

  1. Spring、Spring Boot和TestNG测试指南 - 测试关系型数据库

    Github地址 Spring Test Framework提供了对JDBC的支持,能够让我们很方便对关系型数据库做集成测试. 同时Spring Boot提供了和Flyway的集成支持,能够方便的管理 ...

  2. 2019测试指南-测试测试原理

    2019独角兽企业重金招聘Python工程师标准>>> 什么是测试? 在Web应用程序的开发生命周期中,需要测试许多东西,但测试实际上意味着什么?Merriam-Webster Di ...

  3. boot spring test 文档_Spring、Spring Boot 和 TestNG 测试指南 ( 3 )

    原标题:Spring.Spring Boot 和 TestNG 测试指南 ( 3 ) 来源:chanjarster, github.com/chanjarster/spring-test-exampl ...

  4. Spring Boot微服务的黑匣子测试是如此简单

    当我需要进行原型设计,概念验证或在空闲时间使用一些新技术时,开始新项目对于Maven来说总是有点烦人. 不得不说,设置Maven项目并不难,您可以使用Maven原型. 但是原型通常是过时的. 谁想玩旧 ...

  5. spring boot测试_测试Spring Boot有条件的合理方式

    spring boot测试 如果您或多或少有经验的Spring Boot用户,那么很幸运,在某些时候您可能需要遇到必须有条件地注入特定bean或配置的情况 . 它的机制是很好理解的 ,但有时这样的测试 ...

  6. Spring Boot 和 testNG 和 eclipse背景色

    通过之前的博文,我们已经验证,Spring环境下完成access数据访问没有问题.下面我们直接在Spring环境下部署我们的升级项目. 1.导入Spring Boot Spring Boot是Spri ...

  7. spring项目搭建-注册对象到容器测试

    那还没有进行配置,回到刚才要搭建的项目中来,你是不是要把user对象交给咱们的Spring容器来管理,那就要把User配置到这里面来,那下面看一下,导入完的具体配置,将User对象交给Spring容器 ...

  8. Spring Boot登录选项快速指南

    "我喜欢编写身份验证和授权代码." 〜从来没有Java开发人员. 厌倦了一次又一次地建立相同的登录屏幕? 尝试使用Okta API进行托管身份验证,授权和多因素身份验证. 在本文中 ...

  9. spring框架mvc框架_Spring的MVC测试框架入门–第1部分

    spring框架mvc框架 最新推出的主要Spring框架是Spring MVC测试框架,Spring Guys声称它是"一流的JUnit支持,可通过流畅的API测试客户端和服务器端Spri ...

最新文章

  1. CentOS7-64bit 编译 Hadoop-2.5.0,并分布式安装
  2. mysql dba系统学习(4)mysql的多实例multi启动停止
  3. 全球与中国塑料废料粉碎机市场运营状况分析及投资风险评估报告2022-2027年版
  4. Putty基础教程之(一).入门命令学习及编写HTML
  5. IntelliJ IDEA Maven Gradle的配置
  6. ming window 交叉编译_如何在Linux for Windows上与MinGW交叉编译?
  7. linux下搜狗安装目录,Linux安装搜狗输入法
  8. 女程序员做了个梦。。。
  9. WannaCry病毒横行Windows,Mac用户不应幸灾乐祸!
  10. linux 下后台运行python脚本
  11. go实现文档中保存的微信图片.dat格式解密为图片
  12. 三点式女青年和免费的笔记本电脑
  13. 【C++】C++调用Python
  14. 输出的字符串中有空格并且无法替换B e a r e r N D c y Z T g 4 N z A t N 2 N h Z C 0 0 Y 2 E 4 L T g x M W E t O D Y
  15. 2023电工杯数学建模A题B题
  16. pymysql mysql 1064_python使用pymysql操作MySQL错误代码1054和1064处理
  17. 阿里云共享和独享云虚拟主机该怎么选?
  18. 访问 Linux 服务器上的文件(以图片为例)
  19. 西门子S7-1200PLC脉冲控制伺服程序
  20. php开发微信demo

热门文章

  1. c比java运行快的原因_C/C++比JAVA快的原因
  2. ROS中阶笔记(五):机器人感知—机器视觉
  3. rtsp转rtmp服务linux,ubuntu安装流媒体服务器(nginx+rtmp,rtsp转rtmp,rtsp转m3u8)
  4. 零基础学python尹会生_编程零基础应当如何开始学习 Python?
  5. java 数组详解_java_day03:数组详解
  6. java 调用 rsync_运行rsync时,QProcess不显示任何输出
  7. 【codevs1163】访问艺术馆,圣战の终焉
  8. 服务器安装系统的工具,工具集#08 服务器安装系统指南
  9. 【英语学习】【WOTD】thole 释义/词源/示例
  10. HashMap put原理详解(基于jdk1.8)