8.5.2 使用 @DataR2dbcTest 和 Testcontainers 测试数据持久化
正如你在前面章节中可能回忆的那样,Spring Boot 允许你通过仅加载特定应用程序切片使用的 Spring 组件来运行集成测试。对于 REST API,你将为 WebFlux 切片创建测试。在这里,我将向你展示如何使用 @DataR2dbcTest 注解为 R2DBC 切片编写测试。
方法与你在第 5 章中用于测试 Catalog Service 数据层的方法相同,但有两个主要区别。首先,你将使用 StepVerifier 工具来响应式测试 OrderRepository 行为。其次,你将明确定义一个 PostgreSQL 测试容器实例。
对于 Catalog Service 应用程序,我们依赖于测试容器自动配置。在这种情况下,我们将在测试类中定义一个测试容器实例并将其标记为 @Container。然后类上的 @Testcontainers 注解将激活测试容器的自动启动和清理。最后,我们将使用 Spring Boot 提供的 @DynamicProperties 注解将测试数据库的凭据和 URL 传递给应用程序。这种定义测试容器和覆盖属性的方法是通用的,可以应用于其他场景。
现在开始写代码。创建一个 OrderRepositoryR2dbcTests 类并实现自动测试来验证应用程序的数据持久化层。
代码清单 8.30 Data R2DBC 切片的集成测试
package com.polarbookshop.orderservice.order.domain;
import com.polarbookshop.orderservice.config.DataConfig;
import org.junit.jupiter.api.Test;
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import org.testcontainers.utility.DockerImageName;
import reactor.test.StepVerifier;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.r2dbc.DataR2dbcTest;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
@DataR2dbcTest // 标识专注于 R2DBC 组件的测试类
@Import(DataConfig.class)
@Testcontainers
class OrderRepositoryR2dbcTests {
private static final String POSTGRESQL_IMAGE = "postgres:14.4";
@Container // 标识用于测试的 PostgreSQL 容器
static PostgreSQLContainer<?> postgresql =
new PostgreSQLContainer<>(DockerImageName.parse(POSTGRESQL_IMAGE))
.withDatabaseName("polardb_order")
.withUsername("user")
.withPassword("password");
@DynamicPropertySource
static void postgresqlProperties(DynamicPropertyRegistry registry) {
registry.add("spring.r2dbc.url", () ->
String.format("r2dbc:postgresql://%s:%d/%s",
postgresql.getHost(),
postgresql.getFirstMappedPort(),
postgresql.getDatabaseName()));
registry.add("spring.r2dbc.username", postgresql::getUsername);
registry.add("spring.r2dbc.password", postgresql::getPassword);
}
@Autowired
private OrderRepository orderRepository;
@Test
void whenFindAllThenReturnAllOrders() {
StepVerifier.create(orderRepository.findAll())
.expectNextCount(0)
.verifyComplete();
}
@Test
void whenSaveOrderThenReturnSavedOrder() {
var order = Order.of("1234567890", "Title", "Author", 1, OrderStatus.ACCEPTED);
StepVerifier.create(orderRepository.save(order))
.expectNextMatches(savedOrder ->
savedOrder.id() != null &&
savedOrder.isbn().equals("1234567890"))
.verifyComplete();
}
}
这个测试类:
- 使用 Testcontainers 启动一个真实的 PostgreSQL 容器
- 配置 Spring Data R2DBC 连接到测试数据库
- 测试 OrderRepository 的基本操作
运行测试:
$ ./gradlew test --tests OrderRepositoryR2dbcTests
本章小结
在本章中,你学习了:
- 响应式编程基础——事件循环模型、Reactive Streams、Project Reactor
- Mono 和 Flux——响应式流的两种核心类型
- Spring WebFlux——构建响应式 Web 应用程序
- Spring Data R2DBC——响应式数据持久化
- Spring WebClient——响应式 HTTP 客户端
- 弹性模式——超时、重试和故障转移
- 响应式测试——使用 StepVerifier、MockWebServer 和 Testcontainers
响应式应用程序提供了更好的可扩展性和资源利用率,但也引入了额外的复杂性。在选择是否使用响应式编程时,请权衡利弊。
在下一章中,我们将探讨 API 网关和断路器,这是构建弹性分布式系统的另一个重要方面。