单元测试:SpringBoot Test

2022/9/17 单元测试Java

Spring Boot 2.2.0 版本开始引入 JUnit 5 作为单元测试默认库,在 Spring Boot 2.2.0 版本之前,spring-boot-starter-test 包含了 JUnit 4 的依赖,Spring Boot 2.2.0 版本之后替换成了 Junit Jupiter (没有@RunWith注解了)。

SpringBootTest默认集成了以下功能:

  • JUnit 5: Java单元测试框架
  • Spring Test & Spring Boot Test: Spring Boot的测试工具和支持
  • AssertJ: 流式断言
  • Hamcrest: Hamcrest断言
  • Mockito: Java Mock框架
  • JSONassert: JSON断言
  • JsonPath: XPath for JSON

# 1. 功能分类

从功能上讲,Spring Boot Test中的注解主要分如下几类:

类别 示例 格式 说明
配置类型 @TestConfiguration 提供一些测试相关的配置入口
mock类型 @MockBean 提供mock支持
启动测试类型 @SpringBootTest @*Test 以Test结尾的注解,具有加载applicationContext的能力
自动配置类型 @AutoConfigureJdbc @AutoConfigure* 以AutoConfigure开头的注解,具有加载测试支持功能的能力。

# 1.1 配置类型的注解

注解 作用 实践中的使用
@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。

# 1.2 mock类型的注解

注解 作用
@MockBean 用于mock指定的class或被注解的属性(或者说在测试类中排除一个bean,Spring将使用这个mock而不是真正的类,所以不会调用@PostConstruct方法)
@MockBeans 使@MockBean支持在同一类型或属性上多次出现
@SpyBean 用于spy指定的class或被注解的属性
@SpyBeans 使@SpyBeans支持在同一类型或属性上多次出现

@MockBean@SpyBean这两个注解,在mockito框架中本来已经存在,且功能基本相同。Spring Boot Test又定义一份重复的注解,目的在于使MockBeanSpyBean被ApplicationContext管理,从而方便使用。

MockBean和SpyBean功能非常相似,都能模拟方法的各种行为。不同之处在于MockBean是全新的对象,跟正式对象没有关系;而SpyBean与正式对象紧密联系,可以模拟正式对象的部分方法,没有被模拟的方法仍然可以运行正式代码。

# 1.3 自动配置类型的注解(@AutoConfigure*)

注解 作用
@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

“自动配置类型”有两种使用方式:

  1. 在功能测试(即使用@SpringBootTest)时显示添加。
  2. 一般在切片测试中被隐式使用,例如@WebMvcTest注解时,隐式添加了@AutoConfigureCache@AutoConfigureWebMvc@AutoConfigureMockMvc

实现原理spring-boot-autoconfigure中的@\*AutoConfiguration实现略有不同,Test包中的@AutoConfigure\*通过DeterminableImports接口作为指定代码的识别入口,通过ImportAutoConfiguration注解作为配置入口,从Test包下的spring.factories (opens new window)读取配置文件,每个@AutoConfigure\*中都可以包含多个Spring Boot的@\*AutoConfiguration,例如:

AutoConfigureWebMvc

AutoConfigureWebMvc

# 1.4 启动测试类型的注解(@*Test)

所有的 @*Test 注解都被 @BootstrapWith 注解,它们可以启动 ApplicationContext,是测试的入口,所有的测试类必须声明一个 @*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 (opens new window)查看详情。

一般情况下,推荐使用@SpringBootTest而非其它切片测试的注解,简单有效。若某次改动仅涉及特定切片,可以考虑使用切片测试。

@SpringBootTest是这些注解中最常用的一个,其中包含的配置项如下:

配置名称 说明
value 指定配置属性
properties 指定配置属性,和value意义相同
classes 指定配置类,等同于@ContextConfiguration中的class,若没有显示指定,将查找嵌套的@Configuration类,然后返回到SpringBootConfiguration搜索配置
webEnvironment 指定web环境,可选值有:MOCKRANDOM_PORTDEFINED_PORTNONE

webEnvironment详细说明:

可选值 说明
MOCK 此值为默认值,该类型提供一个mock环境,此时内嵌的服务(servlet容器)并没有真正启动,也不会监听web端口。
RANDOM_PORT 启动一个真实的web服务,监听一个随机端口。
DEFINED_PORT 启动一个真实的web服务,监听一个定义好的端口(从配置中读取)。
NONE 启动一个非web的ApplicationContext,既不提供mock环境,也不提供真是的web服务。

# 2. 相互之间的搭配组合

典型的搭配如下:

package sample.test;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import sample.test.domain.VehicleIdentificationNumber;
import sample.test.service.VehicleDetails;
import sample.test.service.VehicleDetailsService;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.test.context.junit4.SpringRunner;

import static org.mockito.BDDMockito.given;

/**
 * {@code @SpringBootTest} with a random port for {@link SampleTestApplication}.
 *
 * @author Phillip Webb
 */
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@AutoConfigureTestDatabase
public class SampleTestApplicationWebIntegrationTests {

    private static final VehicleIdentificationNumber VIN = new VehicleIdentificationNumber(
            "01234567890123456");

    @Autowired
    private TestRestTemplate restTemplate;

    @MockBean
    private VehicleDetailsService vehicleDetailsService;

    @Before
    public void setup() {
        given(this.vehicleDetailsService.getVehicleDetails(VIN))
                .willReturn(new VehicleDetails("Honda", "Civic"));
    }

    @Test
    public void test() {
        this.restTemplate.getForEntity("/{username}/vehicle", String.class, "sframework");
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
  • @RunWith(SpringRunner.class)是JUnit的注解,作用是关联Spring Boot Test,在运行JUnit时同时启动Spring
  • @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) 作用是启动Spring的ApplicationContext,参数webEnvironment指定了运行的web环境
  • @AutoConfigureTestDatabase 作用是启动一个内存数据库,不使用真实的数据库

其中@RunWith和@Test必须存在,@AutoConfigure可以同时配置任意多个,而配置类型的注解可以在需要时添加。

# 3. 相似注解的区别和联系

# 3.1 @TestComment vs @Comment

  • @TestComponent是另一种@Component,在语义上用来指定某个Bean是专门用于测试的
  • 使用@SpringBootApplication服务时,@TestComponent会被自动排除

# 3.2 @TestConfiguration vs @Configuration

  • @TestConfiguration是Spring Boot Boot Test提供的,@Configuration是Spring Framework提供的。
  • @TestConfiguration实际上是也是一种@TestComponent,只是这个@TestComponent专门用来做配置用。
  • @TestConfiguration@Configuration不同,它不会阻止@SpringBootTest的查找机制,相当于是对既有配置的补充或覆盖。

# 3.3 @SpringBootTest vs @WebMvcTest(或@*Test)

  • 都可以启动 Spring 的 ApplicationContext
  • @SpringBootTest自动侦测并加载@SpringBootApplication@SpringBootConfiguration中的配置,@WebMvcTest不侦测配置,只是默认加载一些自动配置。
  • @SpringBootTest测试范围一般比@WebMvcTest大。

# 4. 小结

本文主要介绍了Spring Boot Test中新增的注解,这些注解分为这几个类型:配置类型、mock类型、启动测试类型、自动配置类型。

  1. “配置类型”中的@TestComponent@TestConfiguration@OverrideAutoConfiguration使配置更加灵活。
  2. 封装了mockito@MockBean@SpyBean,使其可以自然的注入到Spring容器中。
  3. 每个测试类必须包含一个“启动测试类型”的注解(@\*Test),同时可以根据需要添加”自动配置类型”的注解(@AutoConfigure*)。
  4. @SpringBootTest是最常用的“启动测试类型”注解
此生不换
青鸟飞鱼