文档章节

Spring、Spring Boot和TestNG测试指南 - 使用Spring Boot Testing工具

jarchan
 jarchan
发布于 2017/08/28 08:58
字数 1198
阅读 668
收藏 29

Github地址

前面一个部分讲解了如何使用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或其变体,那得多吓人。

参考文档

© 著作权归作者所有

共有 人打赏支持
jarchan

jarchan

粉丝 59
博文 11
码字总数 8798
作品 2
崇明
技术主管
私信 提问
加载中

评论(1)

红薯
红薯
代码托管到码云呗 :)
Spring Boot 和 testNG 和 eclipse背景色

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

pandamonica
2017/09/19
0
0
Spring Boot入门资料整理

Spring Boot 初识 SpringBoot前世今生 本文主要讲述spring boot的由来,即其它诞生的背景,初衷,现状,及对未来的展望。 Spring Boot参考指南中文版--Chapter1.Spring Boot中文文档 本节提供...

小致dad
2017/11/07
0
0
springboot系列二 springcloud和springboot学习资料整理

springboot 作者 链接 springboot官网 https://spring.io/projects/spring-boot springboot官方文档 https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/ springboot官方......

yimingkeji
12/02
0
0
学习 Spring Boot 知识看这一篇就够了

从2016年因为工作原因开始研究 Spring Boot ,先后写了很多关于 Spring Boot 的文章,发表在技术社区、我的博客和我的公号内。粗略的统计了一下总共的文章加起来大概有六十多篇了,其中一部分...

ityouknow
05/28
0
0
Spring Boot学习笔记

RabbitMQ RabbitMQ 安装 linux安装RabbitMQ详细教程 Ubuntu 16.04 RabbitMq 安装与运行(安装篇) ubantu安装rabbitMQ步骤 多线程 Spring @transactional注解和synchronized同步锁同时使用不...

OSC_fly
07/26
0
0

没有更多内容

加载失败,请刷新页面

加载更多

如何在Angular中使用better-scroll插件

由于需要在一个固定的的高度做无限滚动,本来css的overflow-y也可以完成的,奈何安卓不是很流畅,还很生硬,就是用了第三方库better-scroll,配合angular的ng-content。angular的ng-content和...

前端攻城老湿
2分钟前
0
0
Js中apply和Math.max()函数的问题及区别

这篇文章主要介绍了js中apply和Math.max()函数的问题,本文给大家带来两种答案,每一种答案给大家介绍的非常详细,在文章底部给大家提到了js中Math.max.apply和Math.max的区别,写的十分的全...

前端攻城小牛
2分钟前
0
0
解决 SpringBoot 不继承父starter-parent打包不包含依赖的问题

由于项目需要继承自己平台的父 parent , 有的模块是纯 api ,不能有任何依赖, 所以父 parent 不能直接引入 springboot, 单独给非 boot 项目排除依赖的话又特别的麻烦, 且不好把控。 记得刚接触...

stys35
4分钟前
0
0
vuex进阶知识点巩固

我们先回忆一下上一篇的代码 computed:{ getName(){ return this.$store.state.name }} 这里假设现在逻辑有变,我们最终期望得到的数据(getName),是基于 this.$store.state.na...

嫣然丫丫丫
19分钟前
1
0
Python出现安全策略问题的解决方法

Python运行期间出现如下错误 import: attempt to perform an operation not allowed by the security policy `PS' @ error/constitute.c/IsCoderAuthorized/408. 解决方法:在脚本的开头添加......

大糊涂
26分钟前
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部