下载工作台
Spring Cloud 微服务

契约测试与集成测试

试读上半部分 · 解锁后可读全文

第 11 章 · 契约测试与集成测试

本章目标:理解微服务接口契约的重要性;使用 Spring Cloud ContractPact 验证 product-svc 提供者order-svc 消费者 的 Feign 契约;结合 WireMockTestcontainers(Nacos/MySQL)编写集成测试;在 CI 中实现不依赖全套环境的流水线验证;建立 svc-spring-demo 测试金字塔。

学时建议:5~6 小时(含 2 小时 Contract 生成与 CI 配置)

前置spring-cloud-web ch01~ch10spring-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十秒
E2Ecurl 经 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 测试失败,阻止合并。


11.5 WireMock 独立模拟(轻量方案)

以下内容需解锁后阅读

试读已结束。解锁本章 ¥5.00,或开通年度会员畅读全部教程。
年度会员 ¥199.00/年; 小紫 AI 工作台有效会员 ¥99.00/年

正文仅在服务端鉴权后下发,未付费无法获取下半部分内容。