第 11 章 · 契约测试与集成测试
本章目标:理解微服务接口契约的重要性;使用 Spring Cloud Contract 或 Pact 验证 product-svc 提供者与 order-svc 消费者 的 Feign 契约;结合 WireMock、Testcontainers(Nacos/MySQL)编写集成测试;在 CI 中实现不依赖全套环境的流水线验证;建立 svc-spring-demo 测试金字塔。
学时建议:5~6 小时(含 2 小时 Contract 生成与 CI 配置)
前置:spring-cloud-web ch01~ch10;spring-boot-web ch11 MockMvc 测试;Feign 客户端(ch04)。
11.1 微服务测试金字塔
┌─────────┐
│ E2E │ 少量:Gateway + 全服务 + MQ(staging)
├─────────┤
│ 集成测试 │ Testcontainers / WireMock / Contract
├─────────┤
│ 单元测试 │ Service / Domain 纯逻辑
└─────────┘
| 测试类型 | svc-spring-demo 示例 | 速度 |
|---|---|---|
| 单元 | OrderPriceCalculator | 毫秒 |
| 契约 | product API JSON 结构 | 秒级 |
| 集成 | order-svc + WireMock product | 十秒 |
| E2E | curl 经 Gateway 下单 | 分钟 |
痛点:product-svc 改字段名 → order-svc 编译期不报错 → 运行时 500。契约测试在发布前捕获破坏性变更。
11.2 契约测试角色
| 角色 | 职责 | 本课程示例 |
|---|---|---|
| Provider 提供者 | 实现 API,生成契约 | product-svc |
| Consumer 消费者 | 声明期望,验证兼容 | order-svc |
| Broker(Pact) | 共享契约仓库 | 选修 |
product-svc (Provider)
│ 生成 contract: GET /api/v1/products/{id}
│ 响应 JSON 结构
▼
order-svc (Consumer) 测试时加载契约
│ WireMock 回放
▼
OrderCreateServiceTest 通过
11.3 Spring Cloud Contract(Provider 侧)
product-svc pom.xml:
<plugin>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-contract-maven-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<baseClassForTests>com.example.svc.product.contract.BaseContractTest</baseClassForTests>
</configuration>
</plugin>
契约 Groovy 文件 src/test/resources/contracts/product/get_by_id.groovy:
Contract.make {
description "按 ID 查询商品"
request {
method GET()
url "/api/v1/products/1"
headers {
contentType(applicationJson())
}
}
response {
status 200
headers {
contentType(applicationJson())
}
body("""
{
"code": "0",
"message": "ok",
"data": {
"id": 1,
"name": "教学商品",
"price": 99.00,
"coverUrl": "https://cdn.example.com/p1.jpg",
"status": "ON_SALE"
}
}
""")
}
}
BaseContractTest 启动 MockMvc 或 @SpringBootTest:
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
@AutoConfigureMockMvc
public abstract class BaseContractTest {
@Autowired protected MockMvc mockMvc;
@MockBean protected ProductService productService;
@BeforeEach
void stub() {
when(productService.getById(1L)).thenReturn(sampleProduct());
}
}
执行 mvn verify 生成 stub JAR 供 Consumer 使用。
11.4 Consumer 侧验证(order-svc)
order-svc 引入 stub:
<dependency>
<groupId>com.example.svc</groupId>
<artifactId>product-svc</artifactId>
<version>${project.version}</version>
<classifier>stubs</classifier>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-contract-stub-runner</artifactId>
<scope>test</scope>
</dependency>
测试类:
@SpringBootTest
@AutoConfigureStubRunner(
ids = "com.example.svc:product-svc:+:stubs:8082",
stubsMode = StubRunnerProperties.StubsMode.LOCAL
)
class OrderCreateWithContractTest {
@Autowired OrderCreateService orderCreateService;
@Test
void shouldCreateOrderWhenProductExists() {
CreateOrderCommand cmd = new CreateOrderCommand(7L, List.of(
new OrderItemCommand(1L, 2)));
Order order = orderCreateService.create(cmd);
assertThat(order.getStatus()).isEqualTo(OrderStatus.CREATED);
}
}
若 product 契约变更未通知 order 团队,Consumer 测试失败,阻止合并。