# 在 Spring Boot 2 中进行测试

> 原文： [https://howtodoinjava.com/spring-boot2/testing/testing-support/](https://howtodoinjava.com/spring-boot2/testing/testing-support/)

学习在 Spring Boot 应用程序中编写单元测试和集成测试。 了解单元测试和集成测试之间的区别，以及支持此类测试的注解。

## 1\. 单元测试与集成测试

通常，任何软件应用程序都分为不同的模块和组件。 孤立地测试一个这样的组件时，称为单元测试。 编写该代码是为了验证一小段代码是否正在执行预期的操作。

**单元测试**无法验证应用程序代码是否与外部依赖项正确配合。 它专注于单个组件并模拟与该组件交互的所有依赖项。

一旦开发并集成了不同的模块，便会执行集成测试。 其主要目的是发现不同模块相互交互以端对端处理用户请求时的问题。

**集成测试**可以根据要测试的内容，将整个应用程序置于范围内或仅包含某些组件。 他们可能需要为数据库实例和硬件分配资源。 尽管也可以模拟这些交互以提高测试性能。

就典型的[ Spring boot crud 应用程序](https://howtodoinjava.com/spring-boot2/spring-boot-crud-hibernate/)而言，可以编写单元测试来分别测试 REST 控制器，DAO 层等。 甚至不需要[嵌入式服务器](https://howtodoinjava.com/spring-boot2/embedded-tomcat-configuration/)。

在集成测试中，我们将专注于测试从控制器到持久层的完整请求处理。 应用程序应在嵌入式服务器中运行，以创建应用程序上下文和所有 bean。 这些 bean 中的某些可能会被覆盖以模拟某些行为。

## 2\. 依赖项

#### 2.1. Junit 4 测试（默认）

要在 Spring Boot 应用程序中编写测试，最好的方法是在`pom.xml`文件中包含`spring-boot-starter-test`。 它将 [Junit 4](https://howtodoinjava.com/junit-4/)，AssertJ，Hamcrest，[Mockito](https://howtodoinjava.com/mockito/junit-mockito-example/)，JSONassert 和 JsonPath 依赖项带入具有测试范围的应用程序中。

`pom.xml`

```java
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

```

#### 2.2. Junit 5 测试

Spring Boot 也支持 [Junit 5](https://howtodoinjava.com/spring-5-tutorial/) 测试。 要使用 Junit 5，请包含它的依赖项，并从`spring-boot-starter-test`中排除 Junit 4。

编写集成测试时，嵌入式数据库依赖关系非常方便。

`pom.xml`

```java
dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>

    <!-- exclude junit 4 -->
    <exclusions>
        <exclusion>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </exclusion>
    </exclusions>

</dependency>

<!-- junit 5 -->
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-api</artifactId>
    <scope>test</scope>
</dependency>

<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-engine</artifactId>
    <scope>test</scope>
</dependency>

<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>test</scope>
    <version>1.4.194</version>
</dependency>

```

## 3\. 运行器

用 Spring Boot 编写的测试可以多种方式运行。 让我们来看看几种最常见的方式。

#### 3.1. `@RunWith(SpringRunner.class)` – [Junit 4]

默认情况下，编写的测试在 Junit 4 中进行。要运行此类测试，我们可以在类级别使用`@RunWith`注解使用[`SpringRunner`](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/test/context/junit4/SpringRunner.html)类（扩展`SpringJUnit4ClassRunner`）。

`SpringRunner example`

```java
@RunWith(SpringRunner.class)
@WebFluxTest(controllers = EmployeeController.class)
public class EmployeeRestControllerTest {
	//tests
}

```

#### 3.2. `@RunWith(MockitoJUnitRunner.class)` – [Junit 4 with Mockito]

它测试使用`@Mock`对象，首选使用[`MockitoJUnitRunner`](https://static.javadoc.io/org.mockito/mockito-core/2.6.8/org/mockito/junit/MockitoJUnitRunner.html)对象。 它初始化带有 Mock 注解的模拟，因此不需要显式使用`MockitoAnnotations.initMocks(Object)`。在每种测试方法之前都要初始化模拟。

`MockitoJUnitRunner example`

```java
@RunWith(MockitoJUnitRunner.class)
public class EmployeeRestControllerTest 
{
	@Mock
    private Repository repository;
}

```

#### 3.3. `@ExtendWith(SpringExtension.class)` – [Junit 5]

[`SpringExtension`](https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/test/context/junit/jupiter/SpringExtension.html)将 Spring TestContext Framework 集成到 JUnit 5 的 Jupiter 编程模型中。

`SpringExtension example`

```java
//@ExtendWith(SpringExtension.class)  // included in @WebFluxTest
@WebFluxTest(controllers = EmployeeController.class)
@Import(EmployeeService.class)
public class EmployeeControllerTest 
{
	//
}

```

#### 3.4. `@ExtendWith(MockitoExtension.class)` – [Junit 5]

[`MockitoExtension`](https://static.javadoc.io/org.mockito/mockito-junit-jupiter/3.0.0/org/mockito/junit/jupiter/MockitoExtension.html)初始化模拟并处理严格的存根。它等效于`MockitoJUnitRunner`。

大多数测试注解都包含此注解，因此无需明确包含它。

`MockitoExtension example`

```java
@ExtendWith(MockitoExtension.class)
public class EmployeeControllerTest 
{
	//
}

```

## 4\. Spring Boot `*Test`注解

Spring Boot 提供了各种注解，以启用仅与应用程序的某些部分相关的测试基础结构。 它还提供了注解，这些注解也有助于集成测试。 让我们拜访他们。

#### 4.1. `@SpringBootTest`

该注解有助于编写**集成测试**。 它启动嵌入式服务器并完全初始化应用程序上下文。 我们可以使用`@Autowired`注解将依赖项注入测试类中。

我们还可以使用[嵌套`@Configuration`类](https://howtodoinjava.com/spring-boot2/springboot-test-configuration/)或显式`@TestConfiguration`类提供特定于测试的 bean 配置。

它还为不同的`webEnvironment`模式提供支持，并在定义的或随机的端口上监听正在运行的 Web 服务器。 它还注册了[`TestRestTemplate`](https://howtodoinjava.com/spring-boot2/testresttemplate-post-example/)和/或`WebTestClient` bean，用于 Web 测试。

`@SpringBootTest example`

```java
@SpringBootTest(classes = SpringBootDemoApplication.class, 
		webEnvironment = WebEnvironment.RANDOM_PORT)
public class EmployeeControllerIntegrationTests 
{
	@LocalServerPort
	private int port;

	@Autowired
	private TestRestTemplate restTemplate;

	//tests
}

```

> 阅读更多：[`@SpringBootTest`示例](https://howtodoinjava.com/spring-boot2/springboottest-annotation/)

#### 4.2. `@WebMvcTest`

此注解用于 [Spring MVC](https://howtodoinjava.com/spring-mvc-tutorial/) 测试。 它禁用完整的[自动配置](https://howtodoinjava.com/spring-boot/springbootapplication-auto-configuration/)，而仅应用与 MVC 测试相关的配置。

它还会自动配置`MockMvc`实例。 通过将`.class`作为注解属性，我们只能初始化一个 Web 控制器。

`@WebMvcTest example`

```java
@WebMvcTest(EmployeeRESTController.class)
public class TestEmployeeRESTController {

  	@Autowired
  	private MockMvc mvc;

 	//
}

```

> 阅读更多：[`@WebMvcTest`示例](https://howtodoinjava.com/spring-boot2/spring-boot-mockmvc-example/)

#### 4.3. `@WebFluxTest`

该注解禁用了完全自动配置，而是仅应用与 [WebFlux](https://howtodoinjava.com/spring-webflux/spring-webflux-tutorial/) 测试相关的配置。 默认情况下，用`@WebFluxTest`注解的测试也将自动配置`WebTestClient`。

通常，`@WebFluxTest`与[`@MockBean`](https://howtodoinjava.com/spring-boot2/spring-mockbean-annotation/)或`@Import`结合使用以创建控制器 bean 所需的任何协作者。

`@WebMvcTest example`

```java
@WebFluxTest(controllers = EmployeeController.class)
@Import(EmployeeService.class)
public class EmployeeControllerTest 
{
	@MockBean
	EmployeeRepository repository;

	@Autowired
	private WebTestClient webClient;

	//tests
}

```

> 阅读更多：[`@WebFluxTest`示例](https://howtodoinjava.com/spring-webflux/webfluxtest-with-webtestclient/)

#### 4.4. 其他常用注解

*   `@JdbcTest` – 当测试仅针对基于 jdbc 的组件时，可以用于典型的 [jdbc](https://howtodoinjava.com/jdbc-tutorials/) 测试。它禁用完全自动配置，而是仅应用与 jdbc 测试相关的配置。

    默认情况下，用`@JdbcTest`注解的测试是事务性的，并在每个测试结束时回滚。 注解配置内存中嵌入式数据库和`JdbcTemplate`。

*   `@JooqTest` – 当测试仅关注基于 jOOQ 的组件时，可以使用它。 注意，默认情况下，带有`@JooqTest`注解的测试使用应用程序配置的数据库。 要使用嵌入式内存数据库，可以使用 `@AutoConfigureTestDatabase`注解来覆盖这些设置。
*   `@JsonTest` – 当测试仅关注 JSON 序列化时使用。 它初始化`@JsonComponent`，`JacksonTester`，`JsonbTester`和`GsonTester`字段。
*   [`@DataJpaTest`](https://howtodoinjava.com/spring-boot2/testing/datajpatest-annotation/) – 它可以用来测试 JPA 应用程序。 默认情况下，它将扫描`@Entity`类并配置 Spring Data JPA 存储库。 如果在类路径上有嵌入式数据库，它也将配置一个。

    默认情况下，数据 JPA 测试是事务性的，并在每次测试结束时回滚。

    数据 JPA 测试也可以注入`TestEntityManager` bean，它为专门设计用于测试的标准 JPA `EntityManager`提供了替代方法。

*   `@DataMongoTest` – 用于测试 MongoDB 应用程序。 默认情况下，它配置内存嵌入式 MongoDB（如果可用），配置`MongoTemplate`，扫描`@Document`类，并配置 Spring Data MongoDB 存储库。
*   `@DataRedisTest` – 用于测试 Redis 应用程序。 默认情况下，它会扫描`@RedisHash`类并配置 Spring Data Redis 存储库。
*   `@DataLdapTest` – 用于测试 LDAP 应用程序。 默认情况下，它配置内存嵌入式 LDAP（如果可用），配置`LdapTemplate`，扫描`@Entry`类，并配置 Spring Data LDAP 存储库。
*   [`@RestClientTest`](https://howtodoinjava.com/spring-boot2/testing/restclienttest-test-services/) – 用于测试 REST 客户端。 默认情况下，它会自动配置 Jackson，GSON 和 Jsonb 支持，配置`RestTemplateBuilder`，并添加对`MockRestServiceServer`的支持。

## 5\. 测试配置

`@TestConfiguration`是`@Configuration`的特殊形式，可用于定义其他 bean 或测试的自定义项。

在 SpringBoot 中，任何在顶级类中配置并带有`@TestConfiguration`注解的 Bean 将不会通过组件扫描获得。 我们必须使用包含测试用例的类显式注册`@TestConfiguration`类。

最好的是，这些测试配置不会自动成为应用程序主要配置的一部分。 仅可通过以下两种方式之一按需使用它们以包括此附加测试配置，即

#### 5.1. `@Import`注解

它称为将一个或多个配置类导入应用程序上下文或 Spring 测试上下文中。

`Import test config`

```java
@Import(MyTestConfiguration.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class SpringBootDemoApplicationTests
{  
    @LocalServerPort
    int randomServerPort;

    @Autowired
    DataSource datasource;

    //tests
}

```

#### 5.2. 静态嵌套类

我们可以在测试类内部的嵌套类中定义测试配置。 嵌套类可以使用`@Configuration`或`@TestConfiguration`注解进行注解。

*   对于嵌套的`@Configuration`类，将使用给定的配置“代替”应用程序的主要配置。
*   嵌套`@TestConfiguration`类用于“附加”应用程序的主要配置。

`Import test config`

```java
@Import(MyTestConfiguration.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class SpringBootDemoApplicationTests
{  
    @LocalServerPort
    int randomServerPort;

    @Autowired
    DataSource datasource;

    //tests
}

```

## 6\. 模拟

对于使用或不使用 Mockito 的依赖关系，Spring Boot 都提供了出色的支持。

#### 6.1. 带有 Mockito – `@Mock`

`@Mock`用于模拟创建。 它使测试类更具可读性。 在测试类中，要处理模拟注解，必须至少使用一次`MockitoAnnotations.initMocks(testClass)`。

请注意，如果您正在使用`RunWith(MockitoJUnitRunner.class)`，则无需显式使用`MockitoAnnotations.initMocks()`。 在每种测试方法之前都要初始化模拟。

在不需要 Spring 文本上下文的单元测试中使用`@Mock`。

#### 6.2. 没有 Mockito – `@MockBean`

`@MockBean`注解用于将模拟添加到 Spring `ApplicationContext`。它允许模拟类或接口，并记录和验证其行为。

有趣的是，上下文中定义的任何相同类型的现有 bean 都将被该模拟代替。 如果没有定义现有的 bean，将添加一个新的 bean。

`@MockBean`与 Mockito 的`@Mock`类似，但具有 Spring 支持。 我们通常将`@MockBean`与`@WebMvcTest`或`@WebFluxTest`注解一起使用。 这些注解适用于 Web 测试片，并且仅限于单个控制器。

在给定的示例中，我们在模拟`EmployeeRepository` bean。 这样，将调用所有应用程序代码，但将模拟所有存储库交互。

```java
@WebFluxTest(controllers = EmployeeController.class)
@Import(EmployeeService.class)
public class EmployeeControllerTest
{
    @MockBean
    EmployeeRepository repository;

    @Autowired
    private WebTestClient webClient;

    //tests
}

```

## 7\. 结论

Spring Boot 为应用程序及其各种模块的[单元测试](https://howtodoinjava.com/spring-boot2/rest-controller-unit-test-example/)和[集成测试](https://howtodoinjava.com/spring-boot2/spring-integration-testing/)提供了出色的支持。 我们将非常小心地通过使用注解来使用所提供的支持。

使用`@SpringBootTest`注解进行集成测试，而其他自动配置注解则用于特定组件的单元测试。

模拟特定行为是非常普遍的要求，为此，我们可以使用嘲笑者的`@Mock`或 Spring 的x`@MockBean`注解。

将我的问题放在评论部分。

学习愉快！