简介
Spring Boot Test 是在 Spring Test 之上的再次封装,增加了切片测试,增强了 mock 能力。
整体上,Spring Boot Test 支持的测试种类,大致可以分为如下三类
| 类别 | 描述 | 涉及的注解 |
|---|---|---|
| 单元测试 | 在web项目中一般面向方法,编写一般业务代码时,测试成本较大 | @Test |
| 切片测试 | 一般面向难于测试的边界功能,介于单元测试和功能测试之间 | @ExtendWith(SpringExtension.class)、@WebMvcTest 等 |
| 功能测试 | 一般面向某个完整的业务功能,同时也可以使用切面测试中的 mock 能力 | @ExtendWith(SpringExtension.class)、@SpringBootTest 等 |
依赖
若想使用 Spring Boot Test 需要引入 spring-boot-starter-test 依赖包,从 Spring Boot 2.2.0 开始,JUnit 5 替代 JUnit 4 作为 Spring Boot Test 单元测试的默认库,当然 JUnit 5 是兼容 JUnit 4 的,如果已将测试迁移到 JUnit 5,则应排除对 JUnit 4 的支持,如以下所示
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
引入 spring-boot-starter-test 之后,会同时引入以下测试常用的依赖
| 名称 | 简介 |
|---|---|
| JUnit | java 单元测试框架 |
| Spring Test & Spring Boot Test | Spring 的测试支持 |
| AssertJ | 断言库,提供了流式的断言方式 |
| Hamcrest | 匹配对象库 |
| Mockito | mock 框架,可以按类型创建 mock 对象,也支持对于 mock 调用过程的断言 |
| JSONassert | JSON 的断言库 |
| JsonPath | 为 JSON 提供了 XPATH 功能 |
Spring Boot Test 使用场景
依赖注入是 Spring Boot 的主要优点之一,但是在对 Spring Boot 项目进行单元测试时,大都是使用 mock 对象而不是实际的依赖对象,所以单元测试基本用不上 Spring Boot Test 的功能。
Spring Boot Test 的主要应用场景是比单元测试的测试范围大的测试类型,比如切片测试、功能测试等。
在测试类中引入Spring特性
当你对 Spring Boot 项目做功能测试需要 Spring Boot 的特性时,Spring Boot 提供了 @SpringBootTest 注释,
@SpringBootTest 替代了 spring-test 中的 @ContextConfiguration 注解,作用是加载 ApplicationContext,启动 spring 容器
它可以作为标准 Spring-test 的 @ContextConfiguration注释的替代,
如果您使用的是 JUnit 4,除了需要引入 @SpringBootTest 之外还需要将 @RunWith(SpringRunner.class) 到测试中,用于整合 JUnit 和 Spring 。如果您使用的是 JUnit 5,则无需添加 @RunWith(SpringRunner.class), 从 Spring Boot 2.2.0 开始 @SpringBootTest和 @xxxTest 注解中包含了@RunWith(SpringRunner.class) 的等价注解 @ExtendWith(SpringExtension.class)。
在 JUnit 4 中引入 Spring boot,注意各个注解来自的包
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class MyTest {
@Test
public void test() {
}
}
在 JUnit 5 中引入 Spring boot
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class MyTest {
@Test
public void test() {
}
}
@SpringBootTest 注解中包含了@RunWith(SpringRunner.class) 等价注解 @ExtendWith(SpringExtension.class)。
功能测试
使用 @SpringBootTest 后,Spring 将加载所有被管理的 bean,基本等同于启动了整个服务,由于 @SpringBootTest 比较重,一般在功能测试时才使用
通过设置@SpringBootTest 的webEnvironment 属性可以指定 web 的环境,该参数的值一共有四个枚举值
| 名称 | 说明 |
|---|---|
| MOCK | MOCK为默认值,它提供一个 mock 环境,可以和 @AutoConfigureMockMvc 或 @AutoConfigureWebTestClient 搭配使用,开启 Mock 相关的功能。注意此时内嵌的服务(servlet 容器)并没有真正启动,也不会监听 web 服务端口。 |
| RANDOM_PORT | 启动一个真实的 web 服务,监听一个随机端口。 |
| DEFINED_PORT | 启动一个真实的 web 服务,监听一个定义好的端口(从 application.properties 读取)。 |
| NONE | 启动一个非 web 的 ApplicationContext,既不提供 mock 环境,也不提供真实的 web 服务。 |
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.MOCK;
@SpringBootTest(webEnvironment = MOCK)
public class MyTest {
@Test
public void test() {
}
}
如果当前服务的 classpath 中没有包含 web 相关的依赖,
@SpringBootTest将启动一个非 web 的 ApplicationContext,此时指定 webEnvironment 将失去作用
切片测试
切片测试,官网文档称为 “slice” of your application,实际上是对一些特定组件的称呼。这里的 slice 并非单独的类(毕竟普通类只需要基于 JUnit 的单元测试即可),而是介于单元测试和功能测试中间的范围。
slice 是指一些在特定环境下才能执行的模块,比如 MVC 中的 Controller、JDBC 数据库访问、Redis 客户端等,这些模块大多脱离特定环境后不能独立运行,假如 spring 没有为此提供测试支持,开发者只能启动完整服务对这些模块进行测试,这在一些复杂的系统中非常不方便,所以 spring 为这些模块提供了测试支持,使开发者有能力单独对这些模块进行测试。
通过 @xxxTest 开启具体模块的测试支持,开启后 spring 仅加载相关的 bean,无关内容不会被加载。
spring boot 切面测试支持的模块及对应的注解有
| 注解 | 作用 |
|---|---|
@DataRedisTest | 测试对 Redis 操作,自动扫描被 @RedisHash 描述的类,并配置 Spring Data Redis 的库 |
@DataJpaTest | 测试基于 JPA 的数据库操作,同时提供了 TestEntityManager 替代 JPA 的 EntityManager |
@DataJdbcTest | 测试基于 Spring Data JDBC 的数据库操作 |
@JsonTest | 测试 JSON 的序列化和反序列化 |
@WebMvcTest | 测试 Spring MVC 中的 controllers |
@WebFluxTest | 测试 Spring WebFlux 中的 controllers |
@RestClientTest | 测试对 REST 客户端的操作 |
@DataLdapTest | 测试对 LDAP 的操作 |
@DataMongoTest | 测试对 MongoDB 的操作 |
@DataNeo4jTest | 测试对 Neo4j 的操作 |
所有的 @*Test 注解都被 @BootstrapWith 注解,它们可以启动 ApplicationContext,是测试的入口,所有的切面测试类必须声明一个 @*Test 注解。
除了
@SpringBootTest之外的注解都是用来进行切面测试的,他们会默认导入一些自动配置,点击官方 docs 查看详情。
Spring Boot Test 常用注解
Spring Boot Test 中的注解众多,主要分如下几类
| 类型 | 注解 | 说明 |
|---|---|---|
| 配置类型 | @TestConfiguration 等 | 提供一些测试相关的配置入口 |
| mock 类型 | @MockBean 等 | 提供 mock 支持 |
| 启动测试类型 | @SpringBootTest 、 @*Test | 以 Test 结尾的注解,具有加载 applicationContext 的能力 |
| 自动配置类型 | @AutoConfigure* | 以 AutoConfigure 开头的注解,具有加载测试支持功能的能力。 |
配置类型的注解
| 注解 | 作用 | 实践中的使用 |
|---|---|---|
@TestComponent | 该注解另一种 @Component,在语义上用来指定某个 Bean 是专门用于测试的。 | 该注解适用于测试代码和正式混合在一起时,不加载被该注解描述的 Bean,使用不多。 |
@TestConfiguration | 该注解是另一种 @TestComponent,它用于补充额外的 Bean 或覆盖已存在的 Bean | 在不修改正式代码的前提下,使配置更加灵活 |
@TypeExcludeFilters | 用来排除 @TestConfiguration 和 @TestComponent | 适用于测试代码和正式代码混合的场景,使用不多 |
@OverrideAutoConfiguration | 可用于覆盖 @EnableAutoConfiguration,与 ImportAutoConfiguration 结合使用,以限制所加载的自动配置类 | 在不修改正式代码的前提下,提供了修改配置自动配置类的能力 |
@PropertyMapping | 定义 @AutoConfigure* 注解中用到的变量名称,例如在 @AutoConfigureMockMvc 中定义名为 spring.test.mockmvc.webclient.enabled 的变量 | 一般不使用 |
使用
@SpringBootApplication启动测试或者生产代码,被@TestComponent描述的 Bean 会自动被排除掉。如果不是则需要向@SpringBootApplication添加 TypeExcludeFilter。
mock 类型的注解
| 注解 | 作用 |
|---|---|
@MockBean | 用于 mock 指定的 class 或被注解的属性 |
@MockBeans | 使 @MockBean 支持在同一类型或属性上多次出现 |
@SpyBean | 用于 spy 指定的 class 或被注解的属性 |
@SpyBeans | 使 @SpyBeans 支持在同一类型或属性上多次出现 |
@MockBean 和 @SpyBean 这两个注解,在 mockito 框架中本来已经存在,且功能基本相同。Spring Boot Test 又定义一份重复的注解,目的在于使 MockBean 和 SpyBean 被 ApplicationContext 管理,从而方便使用。
MockBean 和 SpyBean 功能非常相似,都能模拟方法的各种行为。不同之处在于 MockBean 是全新的对象,跟正式对象没有关系;而 SpyBean 与正式对象紧密联系,可以模拟正式对象的部分方法,没有被模拟的方法仍然可以运行正式代码。
自动配置类型的注解
| 注解 | 作用 |
|---|---|
@AutoConfigureJdbc | 自动配置 JDBC |
@AutoConfigureCache | 自动配置缓存 |
@AutoConfigureDataLdap | 自动配置 LDAP |
@AutoConfigureJson | 自动配置 JSON |
@AutoConfigureJsonTesters | 自动配置 JsonTester |
@AutoConfigureDataJpa | 自动配置 JPA |
@AutoConfigureTestEntityManager | 自动配置 TestEntityManager |
@AutoConfigureRestDocs | 自动配置 Rest Docs |
@AutoConfigureMockRestServiceServer | 自动配置 MockRestServiceServer |
@AutoConfigureWebClient | 自动配置 WebClient |
@AutoConfigureWebFlux | 自动配置 WebFlux |
@AutoConfigureWebTestClient | 自动配置 WebTestClient |
@AutoConfigureMockMvc | 自动配置 MockMvc |
@AutoConfigureWebMvc | 自动配置 WebMvc |
@AutoConfigureDataNeo4j | 自动配置 Neo4j |
@AutoConfigureDataRedis | 自动配置 Redis |
@AutoConfigureJooq | 自动配置 Jooq |
@AutoConfigureTestDatabase | 自动配置 Test Database,可以使用内存数据库 |
这些注解可以搭配 @\*Test 使用,用于开启在 @\*Test 中未自动配置的功能。例如 @SpringBootTest 和 @AutoConfigureMockMvc 组合后,就可以注入 org.springframework.test.web.servlet.MockMvc。
“自动配置类型” 有两种使用方式:
- 在功能测试(即使用
@SpringBootTest)时显示添加。- 一般在切片测试中被隐式使用,例如
@WebMvcTest注解时,隐式添加了@AutoConfigureCache、@AutoConfigureWebMvc、@AutoConfigureMockMvc。
实现原理 与
spring-boot-autoconfigure中的@\*AutoConfiguration实现略有不同,Test 包中的@AutoConfigure\*通过DeterminableImports接口作为指定代码的识别入口,通过ImportAutoConfiguration注解作为配置入口,从 Test 包下的spring.factories读取配置文件,每个@AutoConfigure\*中都可以包含多个Spring Boot 的@\*AutoConfiguration
启动测试类型的注解
所有的 @*Test 注解都被 @BootstrapWith 注解,它们可以启动 ApplicationContext,是 spring 切面测试的入口,所有的 spring 切面测试类必须声明一个 @*Test 注解。
注解 作用 @SpringBootTest自动侦测并加载 @SpringBootApplication或@SpringBootConfiguration中的配置,默认 web 环境为 MOCK,不监听任何端口@DataRedisTest测试对 Redis 操作,自动扫描被 @RedisHash描述的类,并配置 Spring Data Redis 的库@DataJpaTest测试基于 JPA 的数据库操作,同时提供了 TestEntityManager 替代 JPA 的 EntityManager @DataJdbcTest测试基于 Spring Data JDBC 的数据库操作 @JsonTest测试 JSON 的序列化和反序列化 @WebMvcTest测试 Spring MVC 中的 controllers @WebFluxTest测试 Spring WebFlux 中的 controllers @RestClientTest测试对 REST 客户端的操作 @DataLdapTest测试对 LDAP 的操作 @DataMongoTest测试对 MongoDB 的操作 @DataNeo4jTest测试对 Neo4j 的操作
除了
@SpringBootTest之外的注解都是用来进行切面测试的,他们会默认导入一些自动配置,点击官方 docs 查看详情。
一般情况下,推荐使用 @SpringBootTest 而非其它切片测试的注解,简单有效。若某次改动仅涉及特定切片,可以考虑使用切片测试。
@SpringBootTest 是这些注解中最常用的一个,其中包含的配置项如下:
| 配置名称 | 说明 |
|---|---|
value | 指定配置属性 |
properties | 指定配置属性,和 value 意义相同 |
classes | 指定配置类,等同于 @ContextConfiguration 中的 class,若没有显示指定,将查找嵌套的 @Configuration 类,然后返回到 SpringBootConfiguration 搜索配置 |
webEnvironment | 指定 web 环境,可选值有:MOCK、RANDOM_PORT、DEFINED_PORT、NONE |
webEnvironment 详细说明:
| 可选值 | 说明 |
|---|---|
MOCK | 此值为默认值,该类型提供一个 mock 环境,此时内嵌的服务(servlet 容器)并没有真正启动,也不会监听 web 端口。 |
RANDOM_PORT | 启动一个真实的 web 服务,监听一个随机端口。 |
DEFINED_PORT | 启动一个真实的 web 服务,监听一个定义好的端口(从配置中读取)。 |
NONE | 启动一个非 web 的 ApplicationContext,既不提供 mock 环境,也不提供真是的 web 服务。 |