---
title: 单元测试：SpringBoot Test
date: 2022-09-17
tags:
- 单元测试
- Java
categories:
- 后端 Back-end
isShowComments: true
---

<Boxx/>

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注解了）。

<!-- more -->

[[toc]]

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又定义一份重复的注解，目的在于使`MockBean`和`SpyBean`被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`](https://github.com/spring-projects/spring-boot/blob/master/spring-boot-project/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring.factories)读取配置文件，每个`@AutoConfigure\*`中都可以包含多个Spring Boot的`@\*AutoConfiguration`，例如：
>
> ![AutoConfigureWebMvc](http://ypk1226.com/images/2018/12/autoconfigurewebmvc.png)
>
> **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](https://docs.spring.io/spring-boot/docs/2.1.0.RELEASE/reference/htmlsingle/#test-auto-configuration)查看详情。

一般情况下，推荐使用`@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服务。 |

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

典型的搭配如下：

```java
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");
    }

}
```

- @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`是最常用的“启动测试类型”注解
