第 4 章:测试

本章系统梳理了 Spring Boot 云原生应用的测试体系,涵盖单元测试、集成测试、Mock 技术、测试切片、端到端测试及消费者驱动契约测试等关键实践,帮助开发者构建高质量、可持续交付的微服务系统。

测试的构成

在云原生开发中,测试分为单元测试和集成测试。单元测试关注单个组件的功能,集成测试则验证多个组件或服务间的协作。建议将所有测试代码放在 src/test/{java, resources} 目录,确保每次构建或提交前自动运行所有测试用例。

在 Spring Boot 中进行测试

Spring Boot 支持两类测试:

  • 单元测试:不依赖 Spring 上下文,适合纯 Java 逻辑。
  • 集成测试:依赖 Spring ApplicationContext,验证组件集成。

通过在 pom.xml 添加 spring-boot-starter-test 依赖,即可获得完整的测试支持。Spring Initializr 生成的项目已默认集成。

基本集成测试示例

@SpringBootTest
@RunWith(SpringRunner.class)
public class ApplicationContextTests {
    @Autowired
    private ApplicationContext applicationContext;

    @Test
    public void contextLoads() {
        Assert.assertNotNull(this.applicationContext);
    }
}

@SpringBootTest 自动加载应用上下文,@RunWith(SpringRunner.class) 启用 Spring 测试环境。

Spring Boot 测试目录结构

典型项目结构如下:

- mvnw
- mvnw.cmd
- pom.xml
- src/
  - main/
    - java/com/example/Application.java
    - resources/application.properties
  - test/
    - java/com/example/ApplicationTests.java

主代码和测试代码包结构应一致,便于自动扫描和加载。

集成测试与 Mock 技术

集成测试关注组件协作,常用 Mock 技术隔离外部依赖。Spring Boot 支持 @MockBean 注解,将指定 bean 替换为 Mockito mock 对象,便于模拟外部服务或数据库。

Mock 示例(伪代码)

@RunWith(SpringRunner.class)
public class AccountServiceTests {
    @MockBean
    private UserService userService;
    @MockBean
    private AccountRepository accountRepository;

    @Before
    public void before() {
        accountService = new AccountService(accountRepository, userService);
    }

    @Test
    public void getUserAccountsReturnsSingleAccount() {
        // given: 模拟 repository 和 userService 的行为
        // when: 调用 accountService.getUserAccounts()
        // then: 断言返回结果
    }
}

推荐使用构造函数注入,便于测试时替换依赖。

测试切片与自动化注解

Spring Boot 1.4+ 支持测试切片(Test Slice),可只加载部分自动配置,提升测试效率。常用注解包括:

  • @JsonTest:仅测试 JSON 序列化/反序列化
  • @WebMvcTest:仅测试 MVC 控制器
  • @DataJpaTest:仅测试 JPA repository,自动配置内存数据库
  • @RestClientTest:仅测试 RestTemplate 与远程服务交互

@JsonTest 示例

@RunWith(SpringRunner.class)
@JsonTest
public class UserTests {
    @Autowired
    private JacksonTester<User> json;

    @Test
    public void serializeJson() {
        // 断言序列化结果与预期一致
    }
}

@WebMvcTest 示例

@RunWith(SpringRunner.class)
@WebMvcTest(AccountController.class)
public class AccountControllerTest {
    @Autowired
    private MockMvc mvc;
    @MockBean
    private AccountService accountService;

    @Test
    public void getUserAccountsShouldReturnAccounts() throws Exception {
        // given: 模拟 accountService 行为
        // when: mvc.perform(get("/v1/accounts"))
        // then: 断言 HTTP 响应
    }
}

@DataJpaTest 示例

@RunWith(SpringRunner.class)
@DataJpaTest
public class AccountRepositoryTest {
    @Autowired
    private AccountRepository accountRepository;
    @Autowired
    private TestEntityManager entityManager;

    @Test
    public void findUserAccountsShouldReturnAccounts() {
        // 使用 entityManager 持久化实体
        // 断言 repository 查询结果
    }
}

@RestClientTest 示例

@RunWith(SpringRunner.class)
@RestClientTest(UserService.class)
public class UserServiceTests {
    @Autowired
    private UserService userService;
    @Autowired
    private MockRestServiceServer server;

    @Test
    public void getAuthenticationUserShouldReturnUser() {
        // server.expect(...).andRespond(...)
        // 断言 userService.getAuthenticationUser() 返回结果
    }
}

端到端测试与分布式系统一致性

端到端测试(E2E)验证分布式系统的业务流程和用户体验,确保多个微服务协作无误。分布式状态一致性是核心挑战,需关注最终一致性和状态同步。

first_namelast_nameemailstatus
BobDylan[email protected]PENDING
TaylorSwift[email protected]CONFIRMED
TracyChapman[email protected]ARCHIVED
BrunoMars[email protected]INACTIVE
表 1: 用户状态字段示例

状态字段影响业务流程,不同状态下系统行为不同。分布式环境下需设计合理的测试用例,确保状态最终一致。

消费者驱动契约测试(CDC-T)

CDC-T 通过契约文件描述服务间交互,生产者发布存根(stub),消费者在集成测试中使用存根模拟远程服务。Spring Cloud Contract 提供 CDC-T 支持,自动生成和发布存根。

Spring Cloud Contract 流程简述

  1. 生产者定义契约(Groovy DSL),发布存根到仓库
  2. 消费者集成测试时通过 Stub Runner 加载存根,模拟远程服务
  3. 断言服务间交互符合契约

契约定义示例(伪代码)

org.springframework.cloud.contract.spec.Contract.make {
    request {
        method 'GET'
        url '/uaa/v1/me'
        headers { header('Content-Type': consumer(regex('application/*json*'))) }
    }
    response {
        status 200
        body([
            username: value(producer(regex('[A-Za-z0-9]+'))),
            // ... 其他字段
        ])
        headers { header('Content-Type': value('application/json; charset=UTF-8')) }
    }
}

消费者测试集成存根示例

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.NONE)
@AutoConfigureStubRunner(ids = {"group:user-microservice:stubs:8081"}, workOffline = true)
public class ConsumerDrivenTests {
    @Autowired
    private UserService service;

    @Test
    public void shouldReturnAuthenticatedUser() {
        // 调用 service.getAuthenticatedUser(),断言结果符合契约
    }
}

总结

本章系统梳理了 Spring Boot 测试体系,包括单元测试、集成测试、Mock 技术、测试切片、端到端测试和消费者驱动契约测试。通过合理设计和自动化测试,开发者可持续保障微服务系统的质量和可交付性,为云原生架构下的持续交付和高效运维打下坚实基础。

文章导航

独立页面

这是书籍中的独立页面。

书籍首页

评论区