首页 > 编程笔记

Spring Boot Test(测试模块)详解

Spring Boot 提供了许多公用方法与注解,可以帮助开发者测试应用程序。Spring Boot 主要包括 spring-boot-test 与 spring-boot-test-autoconfigure 核心模块。Spring Boot 提供了 spring-boot-starter-test 的 Starter,主要集成了 JUnit Jupiter、AssertJ 和Hamcrest 等常用测试框架。

Spring Boot Test 简介

在 Spring Boot Test 诞生之前,常用的测试框架是 JUnit 等。Spring Boot Test 诞生后,集成了上述测试框架。Spring 框架的一个主要优势是更容易集成单元测试,可以通过 new 操作符直接生成实例,或者用 mock 对象代替真实的依赖。通常,测试不只是单元测试,还有集成测试,Spring Boot Test 可以在不部署应用程序的前提下进行集成测试。

使用 Spring Boot Test,需要在项目中增加 spring-boot-starter-test 的 Starter 依赖,具体如下:
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-test</artifactId>
  <scope>test</scope>
</dependency>
使用 @SpringBootTest 注解,即可进行测试。

如果项目中依赖 spring-boot-starter-test,则自动添加以下类库。如表 1 所示:

表1 测试依赖
说  明
JUnit 包括JUnit 4和JUnit 5
Spring Test & Spring Boot Test 用于Spring Boot测试
AssertJ 流式的断言库
Hamcrest 匹配库
Mockito Mock框架
JSONassert 为JSON提供断言
JsonPath 为JSON提供XPATH功能

通常情况下,Spring Boot Test 支持的测试种类可以分为以下 3 种:
  1. 单元测试:主要用于测试类功能等。
  2. 切片测试:介于单元测试与集成测试之间,在特定环境下才能执行。
  3. 集成测试:测试一个完整的功能逻辑。

核心注解

为了避免复杂的配置,Spring 引入了大量的注解方式进行测试,这样可以减轻很多工作量。让读者理解并学会使用这些注解是本文学习的主要目标,了解这些注解,可以帮助读者更加容易地掌握 Spring Boot Test 的整个框架。

1. @SpringBootTest注解

Spring Boot 用 @SpringBootTest 注解替代了 spring-test 中的 @ContextConfiguration 注解,该注解可以创建 ApplicationContext,而且还添加了一些其他注解来测试特定的应用。

使用 @SpringBootTest 的 WebEnvironment 属性来修改测试的运行方式。

示例代码如下:
import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.Web
Environment;
import org.springframework.boot.test.web.client.TestRestTemplate;

import static org.assertj.core.api.Assertions.assertThat;
//定义测试类
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class RandomPortTestRestTemplateExampleTests {

    @Test
    void exampleTest(@Autowired TestRestTemplate restTemplate) {
        String body = restTemplate.getForObject("/", String.class);
        assertThat(body).isEqualTo("Hello World");      //返回结果断言
    }
}

2. @RunWith注解

Spring Boot Test 默认使用 JUnit 5 框架,@RunWith(SpringRunner.class) 注解可方便开发者使用 JUnit 4 框架。使用方式如下:
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class DemoApplicationTests { ... }

3. @WebMvcTest注解

如果要测试 Spring MVC controllers 是否按预期那样工作,则用 @WebMvcTest 注解。@WebMvcTest 注解可自动配置 Spring MVC,并会限制扫描 @Controller 和 @ControllerAdvice 等注解的 Bean。

通常,@WebMvcTest 仅限于单个 Controller,并结合 @MockBean 注解提供对某个类的模拟实现。@WebMvcTest 还会自动配置MockMvc。MockMvc 提供了一个强大的方法可以快速测试 MVC 控制器,并且无须启动一个完整的 HTTP 服务器。示例代码如下:
import org.junit.jupiter.api.*;
import org.springframework.beans.factory.annotation.*;
import org.springframework.boot.test.autoconfigure.web.servlet.*;
import org.springframework.boot.test.mock.mockito.*;

import static org.assertj.core.api.Assertions.*;
import static org.mockito.BDDMockito.*;
import static org.springframework.test.web.servlet.request.MockMvc
RequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvc
ResultMatchers.*;

@WebMvcTest(UserVehicleController.class)
class MyControllerTests {

    @Autowired
    private MockMvc mvc;   //注入MockMvc

    @MockBean
    private UserVehicleService userVehicleService;

    @Test
    void testExample() throws Exception {
        given(this.userVehicleService.getVehicleDetails("sboot")).willReturn(new VehicleDetails("Honda", "Civic"));
        this.mvc.perform(get("/sboot/vehicle").accept(MediaType.TEXT_PLAIN)).andExpect(status().isOk()).andExpect(content().string("Honda Civic"));
    }
}

4. @WebFluxTest注解

@DataJpaTest 注解可以测试 JPA 应用。默认情况下,该注解会扫描 @Entity 注解的类及 repositories 类。@DataJpaTest 注解不会扫描 @Component 和 @Configuration-Properties 注解的对象。示例代码如下:
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.autoconfigure.orm.jpa.*;

import static org.assertj.core.api.Assertions.*;
//JPA测试
@DataJpaTest
class ExampleRepositoryTests {

    @Autowired
    private TestEntityManager entityManager;

    @Autowired
    private UserRepository repository;

    @Test
    void testExample() throws Exception {
        this.entityManager.persist(new User("sboot", "1234"));
        User user = this.repository.findByUsername("sboot");
        assertThat(user.getUsername()).isEqualTo("sboot");
        //断言用户名
        assertThat(user.getVin()).isEqualTo("1234");
    }
}

5. @DataMongoTest注解

@DataMongoTest 注解可以用来测试 MongoDB 程序。默认会配置一个嵌入的 MongoDB 并配置一个 MongoTemplate 对象,然后扫描 @Document 注解类。示例代码如下:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest;
import org.springframework.data.mongodb.core.MongoTemplate;
//Mongo测试
@DataMongoTest
class ExampleDataMongoTests {

    @Autowired
    private MongoTemplate mongoTemplate;
    ...
}

6. @DataRedisTest注解

@DataRedisTest 注解用来测试 Redis 应用程序。示例代码如下:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.redis.DataRedisTest;
//Redis测试
@DataRedisTest
class ExampleDataRedisTests {

    @Autowired
    private YourRepository repository;
    ...
}

7. @RestClientTest注解

@RestClientTest 注解用来测试 REST clients。默认情况下会自动配置 Jackson、GSON、Jsonb、RestTemplateBuilder,以及对MockRestServiceServer 的支持。示例代码如下:
@RestClientTest(RemoteVehicleDetailsService.class)
class ExampleRestClientTest {

    @Autowired
    private RemoteVehicleDetailsService service;

    @Autowired
    private MockRestServiceServer server;

    @Test
    void getVehicleDetailsWhenResultIsSuccessShouldReturnDetails()
            throws Exception {
        this.server.expect(requestTo("/greet/details")).andRespond(withSuccess("hello", MediaType.TEXT_PLAIN));
        String greeting = this.service.callRestService();
        assertThat(greeting).isEqualTo("hello");
    }
}

8. @AutoConfigureMockMvc注解

@SpringBootTest 注解通常不会启动服务器,如果在测试中用 Web 端点进行测试,可以添加 MockMvc 配置。示例代码如下:
import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MockMvc;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@SpringBootTest
@AutoConfigureMockMvc
class MockMvcExampleTests {

    @Test
    void exampleTest(@Autowired MockMvc mvc) throws Exception {
        mvc.perform(get("/")).andExpect(status().isOk()).andExpect(content().string("Hello World"));
    }
}

9. @MockBean注解

测试的过程中某些场景需要模拟一些组件,这时就需要使用 @MockBean 注解。示例代码如下:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.*;
import org.springframework.boot.test.context.*;
import org.springframework.boot.test.mock.mockito.*;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.BDDMockito.*;

@SpringBootTest
class MyTests {

    @MockBean
    private RemoteService remoteService;

    @Autowired
    private Reverser reverser;

    @Test
    void exampleTest() {
        given(this.remoteService.someCall()).willReturn("mock");
        String reverse = reverser.reverseSomeCall();
        assertThat(reverse).isEqualTo("kcom");
    }
}

优秀文章