1. 概述

本文将展示如何使用 TestcontainersQuarkus 应用进行"真实"的服务间测试。

在微服务架构中,测试至关重要,但重现生产环境往往很困难。常见的测试方案包括:

  • 使用 Postman 等手动 API 测试工具
  • 模拟服务依赖(Mocking)
  • 依赖内存数据库

⚠️ 这些方案可能导致服务间的真实交互直到 CI 阶段或部署到生产环境才被验证,容易造成交付延迟。

Testcontainers 提供了优雅的解决方案:它能在本地测试环境中动态启动依赖服务,让我们直接与容器化的应用、服务或依赖进行交互测试。

2. 解决方案架构

我们的架构设计简洁明了,专注于验证 Testcontainers 在服务间测试中的价值:

解决方案架构:客户端调用被测系统 customer-service,后者再调用 order-service

核心流程:

  1. 客户端调用 customer-service(被测系统)
  2. customer-serviceorder-service 获取客户订单数据
  3. 每个服务都有独立的 PostgreSQL 数据库支撑

3. Testcontainers 实现方案

3.1 添加依赖

首先确保添加以下核心依赖(Maven):

<dependency>
    <groupId>org.testcontainers</groupId>
    <artifactId>testcontainers</artifactId>
    <version>1.19.6</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.testcontainers</groupId>
    <artifactId>postgresql</artifactId>
    <version>1.19.6</version>
    <scope>test</scope>
</dependency>

3.2 测试资源管理器

Quarkus 提供了关键的测试管理类。根据官方文档:

"在测试启动前,常需要启动 Quarkus 应用依赖的服务。为此,Quarkus 提供了 @QuarkusTestResourceQuarkusTestResourceLifecycleManager"

创建测试资源管理器类:

public class CustomerServiceTestcontainersManager implements QuarkusTestResourceLifecycleManager {

}

3.3 容器配置

使用 Testcontainers API 预配置的模块来管理依赖:

private PostgreSQLContainer<?> postgreSQLContainer;
private GenericContainer<?> orderService;

实现服务启动逻辑:

@Override
public Map<String, String> start() {
    // 容器启动配置代码(原文未展开)
}

测试结束后清理资源:

@Override
public void stop() {
    if (orderService != null) {
        orderService.stop();
    }
    if (postgreSQLContainer != null) {
        postgreSQLContainer.stop();
    }
}

3.4 测试类集成

通过注解让 Quarkus 自动管理依赖生命周期:

@QuarkusTestResource(CustomerServiceTestcontainersManager.class)
class CustomerResourceLiveTest {

}

3.5 参数化测试

编写测试验证客户数据及订单获取:

@ParameterizedTest
@MethodSource(value = "customerDataProvider")
void givenCustomer_whenFindById_thenReturnOrders(long customerId, String customerName, int orderSize) {
    Customer response = RestAssured.given()
      .pathParam("id", customerId)
      .get()
      .thenReturn()
      .as(Customer.class);

    Assertions.assertEquals(customerId, response.id);
    Assertions.assertEquals(customerName, response.name);
    Assertions.assertEquals(orderSize, response.orders.size());
}

private static Stream<Arguments> customerDataProvider() {
    return Stream.of(Arguments.of(1, "Customer 1", 3), Arguments.of(2, "Customer 2", 1), Arguments.of(3, "Customer 3", 0));
}

3.6 执行结果

测试运行时输出显示 order-service 容器成功启动:

Creating container for image: quarkus/order-service-jvm:latest
Container quarkus/order-service-jvm:latest is starting: 02ae38053012336ac577860997f74391eef3d4d5cd07cfffba5e27c66f520d9a
Container quarkus/order-service-jvm:latest started in PT1.199365S

✅ 我们成功实现了生产级别的真实依赖测试,端到端验证了服务行为。

4. 总结

本文展示了 Testcontainers 如何通过容器化依赖解决 Quarkus 应用的测试难题。核心优势包括:

  • ✅ 直接与真实服务交互,保证测试可靠性
  • ✅ 提供编程式 API 简化测试代码
  • ✅ 避免模拟服务带来的测试偏差
  • ✅ 支持任意容器化依赖

这种方案踩坑少、简单粗暴,特别适合微服务架构的集成测试场景。

完整源码可在 GitHub 获取。


原始标题:Testing a Quarkus Application Using Testcontainers for Service Dependencies | Baeldung