http://www.infoq.com/cn/articles/Unit-Testing-Complete-Integration-Testing-Begins

https://segmentfault.com/a/1190000010854538

前面一个部分讲解了如何使用Spring Testing工具来测试Spring项目,现在我们讲解如何使用Spring Boot Testing工具来测试Spring Boot项目。

在Spring Boot项目里既可以使用Spring Boot Testing工具,也可以使用Spring Testing工具。
在Spring项目里,一般使用Spring Testing工具,虽然理论上也可以使用Spring Boot Testing,不过因为Spring Boot Testing工具会引入Spring Boot的一些特性比如AutoConfiguration,这可能会给你的测试带来一些奇怪的问题,所以一般不推荐这样做。

例子1:直接加载Bean

使用Spring Boot Testing工具只需要将@ContextConfiguration改成@SpringBootTest即可,源代码见FooServiceImpltest

@SpringBootTest(classes = FooServiceImpl.class)
public class FooServiceImplTest extends AbstractTestNGSpringContextTests { @Autowired
private FooService foo; @Test
public void testPlusCount() throws Exception {
assertEquals(foo.getCount(), 0); foo.plusCount();
assertEquals(foo.getCount(), 1);
} }

例子2:使用内嵌@Configuration加载Bean

源代码见FooServiceImpltest

@SpringBootTest
public class FooServiceImplTest extends AbstractTestNGSpringContextTests { @Autowired
private FooService foo; @Test
public void testPlusCount() throws Exception {
assertEquals(foo.getCount(), 0); foo.plusCount();
assertEquals(foo.getCount(), 1);
} @Configuration
@Import(FooServiceImpl.class)
static class Config {
} }

例子3:使用外部@Configuration加载Bean

Config

@Configuration
@Import(FooServiceImpl.class)
public class Config {
}

FooServiceImpltest

@SpringBootTest(classes = Config.class)
public class FooServiceImplTest extends AbstractTestNGSpringContextTests { @Autowired
private FooService foo; @Test
public void testPlusCount() throws Exception {
assertEquals(foo.getCount(), 0); foo.plusCount();
assertEquals(foo.getCount(), 1);
} }

这个例子和例子2差不多,只不过将@Configuration放到了外部。

例子4:使用@SpringBootConfiguration

前面的例子@SpringBootTest的用法和@ContextConfiguration差不多。不过根据@SpringBootTest文档

  1. 它会尝试加载@SpringBootTest(classes=...)的定义的Annotated classes。Annotated classes的定义在ContextConfiguration中有说明。

  2. 如果没有设定@SpringBootTest(classes=...),那么会去找当前测试类的nested @Configuration class

  3. 如果上一步找到,则会尝试查找@SpringBootConfiguration,查找的路径有:1)看当前测试类是否@SpringBootConfiguration,2)在当前测试类所在的package里找。

所以我们可以利用这个特性来进一步简化测试代码。

Config

@SpringBootConfiguration
@Import(FooServiceImpl.class)
public class Config {
}

FooServiceImpltest

@SpringBootTest
public class FooServiceImplTest extends AbstractTestNGSpringContextTests { @Autowired
private FooService foo; @Test
public void testPlusCount() throws Exception {
assertEquals(foo.getCount(), 0); foo.plusCount();
assertEquals(foo.getCount(), 1);
} }

例子5:使用@ComponentScan扫描Bean

前面的例子我们都使用@Import来加载Bean,虽然这中方法很精确,但是在大型项目中很麻烦。

在常规的Spring Boot项目中,一般都是依靠自动扫描机制来加载Bean的,所以我们希望我们的测试代码也能够利用自动扫描机制来加载Bean。

Config

@SpringBootConfiguration
@ComponentScan(basePackages = "me.chanjar.basic.service")
public class Config {
}

FooServiceImpltest

@SpringBootTest
public class FooServiceImplTest extends AbstractTestNGSpringContextTests { @Autowired
private FooService foo; @Test
public void testPlusCount() throws Exception {
assertEquals(foo.getCount(), 0); foo.plusCount();
assertEquals(foo.getCount(), 1);
} }

例子6:使用@SpringBootApplication

也可以在测试代码上使用@SpringBootApplication,它有这么几个好处:

  1. 自身SpringBootConfiguration

  2. 提供了@ComponentScan配置,以及默认的excludeFilter,有了这些filter Spring在初始化ApplicationContext的时候会排除掉某些Bean和@Configuration

  3. 启用了EnableAutoConfiguration,这个特性能够利用Spring Boot来自动化配置所需要的外部资源,比如数据库、JMS什么的,这在集成测试的时候非常有用。

Config

@SpringBootApplication(scanBasePackages = "me.chanjar.basic.service")
public class Config {
}

FooServiceImpltest

@SpringBootTest
public class FooServiceImplTest extends AbstractTestNGSpringContextTests { @Autowired
private FooService foo; @Test
public void testPlusCount() throws Exception {
assertEquals(foo.getCount(), 0); foo.plusCount();
assertEquals(foo.getCount(), 1);
} }

避免@SpringBootConfiguration冲突

@SpringBootTest没有定义(classes=...,且没有找到nested @Configuration class的情况下,会尝试查询@SpringBootConfiguration,如果找到多个的话则会抛出异常:

Caused by: java.lang.IllegalStateException: Found multiple @SpringBootConfiguration annotated classes [Generic bean: class [...]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in file [/Users/qianjia/workspace-os/spring-test-examples/basic/target/test-classes/me/chanjar/basic/springboot/ex7/FooServiceImplTest1.class], Generic bean: class[me.chanjar.basic.springboot.ex7.FooServiceImplTest2]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in file [...]]

比如以下代码就会造成这个问题:

@SpringBootApplication(scanBasePackages = "me.chanjar.basic.service")
public class Config1 {
} @SpringBootApplication(scanBasePackages = "me.chanjar.basic.service")
public class Config2 {
} @SpringBootTest
public class FooServiceImplTest extends AbstractTestNGSpringContextTests {
// ...
}

解决这个问题的方法有就是避免自动查询@SpringBootConfiguration

  1. 定义@SpringBootTest(classes=...)

  2. 提供nested @Configuration class

最佳实践

除了单元测试(不需要初始化ApplicationContext的测试)外,尽量将测试配置和生产配置保持一致。比如如果生产配置里启用了AutoConfiguration,那么测试配置也应该启用。因为只有这样才能够在测试环境下发现生产环境的问题,也避免出现一些因为配置不同导致的奇怪问题。

在测试代码之间尽量做到配置共用,这么做的优点有3个:

  1. 能够有效利用Spring TestContext Framework的缓存机制,ApplicationContext只会创建一次,后面的测试会直接用已创建的那个,加快测试代码运行速度。

  2. 当项目中的Bean很多的时候,这么做能够降低测试代码复杂度,想想如果每个测试代码都有一套自己的@Configuration或其变体,那得多吓人。

参考文档

例子7:

使用

@ContextHierarchy({
@ContextConfiguration(classes = {ServiceTestConfig.class}, loader = AnnotationConfigContextLoader.class)
})
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
  loader=AnnotationConfigContextLoader.class,
  classes=ApplicationContextTestResourceNameType.class)
public class FieldResourceInjectionTest {
 
    @Resource(name="namedFile")
    private File defaultFile;
 
    @Test
    public void givenResourceAnnotation_WhenOnField_ThenDependencyValid(){
        assertNotNull(defaultFile);
        assertEquals("namedFile.txt", defaultFile.getName());
    }
}
@Configuration
public class ApplicationContextTestResourceNameType {
 
    @Bean(name="namedFile")
    public File namedFile() {
        File namedFile = new File("namedFile.txt");
        return namedFile;
    }
}
 
参考 
http://www.baeldung.com/spring-annotations-resource-inject-autowire

Springboot UT 引入某些类的更多相关文章

  1. Thinkphp 零散知识点(caa/js路径,引入第三方类,ajax返回,session/cookie)

    一.关于JS和CSS路径问题 1.找路径是从入口文件index.php来找的,而不是从文件本身所在位置来找, 因为我们访问时是访问的入口文件 2.在存放JS和CSS的时候可以放到public文件夹下 ...

  2. 尚硅谷springboot学习5-主入口类说明

    package com.atguigu; import org.springframework.boot.SpringApplication; import org.springframework.b ...

  3. SpringBoot启动加载类ApplicationRunner

    SpringBoot启动加载类ApplicationRunner 有时希望项目在启动的时候加载一些系统参数,就要用到ApplicationRunner ApplicationRunner是一个接口,我 ...

  4. springboot服务引入外部jar包在windows运行正常,在linux环境上无法加载到引入jar包的类

    一.问题描述 最近开发了一个springboot程序,需要依赖第三方jar包,这个jar包无法直接通过pom远程仓库下载,需要从自己本地引入,于是配置pom文件如下:将本地jar包引入工程,syste ...

  5. springboot中引入zookeeper,生成 配置类

    1.application.properties zookeeper.address=@mvn.zookeeper.address@ zookeeper.namespace=@mvn.zookeepe ...

  6. SSM项目 以及 springboot 中引入swagger2的方法

    swagger2是一个非常好用的接口文档,在开发的过程中方便前后端接口的交接. 下面我们就来讲讲在使用java时,分别在SSM框架,以及springboot+mybatis框架中引入swagger2的 ...

  7. SpringBoot入门02-配置类

    引入 Spring Boot的底层已经有了Spring MVC Spring Boot习惯优先的思想,很多配置都是可省的 不需要配置web.xml文件 不需要服务层的xml配置 不需要dao层的xml ...

  8. SpringBoot内置工具类,告别瞎写工具类了

    不知大家有没有注意到,接手的项目中存在多个重复的工具类,发现其中很多功能,Spring 自带的都有.于是整理了本文,希望能够帮助到大家! 一.断言 断言是一个逻辑判断,用于检查不应该发生的情况 Ass ...

  9. 引入DecimalFormat类进行数字格式化操作

    引入语句:import java.text.DecimalFormat; 首先创建DecimalFormat类对象,利用类对象调用Format()方法进行格式化操作.这里有两种方法:①.Decimal ...

随机推荐

  1. 基于springboot的spring JSR validation 后台参数验证

    springboot集成JSR参数校验: 1. 导入maven <dependency> <groupId>org.springframework.boot</group ...

  2. MySQL数据库事务各隔离级别加锁情况--read committed && MVCC(转)

    本文转自https://m.imooc.com/article/details?article_id=17290 感谢作者 上篇记录了我对MySQL 事务 隔离级别read uncommitted的理 ...

  3. 跟我一起学extjs5(08--自己定义菜单1)

    跟我一起学extjs5(08--自己定义菜单1) 顶部和底部区域已经作好,在顶部区域有一个菜单的button.这一节我们设计一个菜单的数据结构,使其能够展示出不相同式的菜单.因为准备搭建的是一个系统模 ...

  4. NodeJS学习资料合集

    1. 官网 nodejs 2.  How do I get started with Node.js,stackoverflow提问,收集非常多实用的网站 3.  node-books.github收 ...

  5. Nginx和Tomcat负载均衡实现session共享

    以前的项目使用Nginx作为反向代理实现了多个Tomcat的负载均衡,为了实现多个Tomcat之间的session共享,使用了开源的Memcached-Session-Manager框架. 此框架的优 ...

  6. 解决Sqoop报错Could not load db driver class: com.intersys.jdbc.CacheDriver

    报错栈: // :: INFO tool.CodeGenTool: Beginning code generation // :: ERROR sqoop.Sqoop: Got exception r ...

  7. 采用Operator-sdk轻松将helm chart转为Operator

    去年就接触Operator,从Oracle发布的WebLogic Operator到mySQL Operator,构建的源码一大堆,但感觉一直缺少合适的开发框架能够避免复杂性快速生成, 随着技术的日益 ...

  8. ELK kibana查询与过滤

    在kibana中,可通过搜索查询过滤事务或者在visualization界面点击元素过滤. 创建查询 在Discover界面的搜索栏输入要查询的字段.查询语法是基于Lucene的查询语法.允许布尔运算 ...

  9. 给定任意字符串,计算一共能组合成多少个单词bing

    CSDN编程挑战里的题目 例如有一个字符串"iinbinbing",截取不同位置的字符‘b’.‘i’.‘n’.‘g’组合成单词"bing".若从1开始计数的话, ...

  10. [Linux]在终端启动程序关闭终端不退出的方法

    一般情况下关闭终端时,那么在这个终端中启动的后台程序也会终止,要使终端关闭后,后台程序保持执行,使用这个指令: nohup 命令 & 如:nohup test.sh & 回车,然后提示 ...