ARCHIVE / INITIALIZING000%
正在载入档案界面SYS.07
Interview Prep

集成与契约测试

真实数据库、缓存、消息队列、HTTP/gRPC 与 MCP 边界的测试方法,以及 Consumer-driven Contract、Fixture、隔离和清理。

集成与契约测试

单元测试可以证明代码在假定的外部系统行为下能够工作;集成测试用于验证真实外部系统是否符合该假定。SQL语法、事务隔离、Redis TTL、Kafka Rebalance、HTTP Header和MCP Schema都可能在Fake实现与真实依赖之间存在差异。

1. 集成测试的范围

狭义集成测试一次验证一个边界:

Repository ↔ PostgreSQL
Cache Adapter ↔ Redis
Producer / Consumer ↔ Kafka
HTTP Client ↔ Fake or Test Provider
MCP Client ↔ Real MCP Server

广义集成测试可能启动一组服务。两种定义都可以使用,但报告必须明确SUT、真实依赖和替身,不能仅使用integration目录名称表示不同置信度的测试。

2. 为什么优先使用真实依赖

In-memory Fake通常无法忠实模拟:

  • PostgreSQL MVCC、行锁、唯一约束、SKIP LOCKED;
  • Redis Lua原子性、TTL、Eviction、Stream Consumer Group;
  • Kafka Partition、Offset、Rebalance、事务Producer;
  • HTTP Chunking、Header大小、代理Timeout;
  • gRPC状态码、Deadline与Streaming Flow Control。

能在Container里快速启动的依赖,优先跑真实版本。Fake保留给单测中的错误注入和高频反馈。

3. 数据库集成测试

至少覆盖:

  1. Migration从空库能执行;
  2. Repository真实SQL能读写;
  3. Unique / Foreign Key / Check约束生效;
  4. 事务失败能Rollback;
  5. 并发更新、锁等待与隔离级别符合预期;
  6. Query Plan和索引满足高风险查询;
  7. 时区、NULL、Decimal、JSONB等类型正确映射。

Transactional Outbox例子

测试不能只断言业务表有数据:

BEGIN
  INSERT incident ...
  INSERT outbox(event_id, aggregate_id, payload) ...
COMMIT

应覆盖:

  • 两条写入要么都提交,要么都回滚;
  • Relay并发使用FOR UPDATE SKIP LOCKED不重复认领;
  • 发布成功但标记失败时,重试产生重复消息,下游可幂等;
  • Poison Message进入Dead Letter或人工处理,不无限热循环。

隔离与清理

常见策略:每Case事务回滚、每Case独立Schema、随机数据库名、Container per suite。选择取决于是否需要测试Commit后行为、连接池和跨事务并发。只靠DELETE FROM清理时要处理外键、Sequence和失败中断。

4. Redis集成测试

测试真实Redis时关注:

  • TTL是否在预期时间范围,而不是精确到毫秒;
  • SET NX PX和Lua Compare-and-delete;
  • Stream Pending、ACK、Claim与重复投递;
  • 序列化兼容和Key Namespace;
  • Cache Miss、旧值、穿透与失效;
  • Redis不可用时系统是Fail-open还是Fail-closed。

Fake Clock不能控制真实Redis时间。对TTL测试使用最终一致断言和合理Deadline,不要sleep(1s)后押宝调度器一生顺遂。

5. Kafka集成测试

Producer测试:Key路由、Header、压缩、重试、幂等、事务边界。Consumer测试:Deserialize、Offset提交、重复消息、乱序、Rebalance和Poison Record。

一个够用的场景:

Produce event_id=E1
→ Consumer处理业务写
→ 在Offset提交前模拟崩溃
→ 重启后再次收到E1
→ 业务唯一约束阻止重复副作用
→ Offset最终提交

这验证的是At-least-once现实,不是把enable.idempotence=true念成Exactly Once咒语。

6. HTTP与gRPC边界

Client Integration Test验证:

  • URL、Method、Header和Body序列化;
  • Status / gRPC Code到领域错误的映射;
  • Timeout、Cancellation和Retry;
  • Pagination、Streaming与大Body限制;
  • Unknown Field与向后兼容;
  • TLS、认证和Token刷新。

可以使用本地Fake Server精确模拟Malformed JSON、慢Header、不完整Body和连接重置。它比Mock一个Do(req)返回值更接近真实网络边界,但仍需使用Contract Test验证Fake与真实API契约一致。

7. Contract Test解决什么

Contract Test验证Consumer依赖的最小交互与Provider实际行为一致。Consumer-driven Contract流程:

Consumer用Mock Provider写期望
→ 生成Contract Artifact
→ 发布到Broker
→ Provider拉取Contract
→ 在指定Provider State下重放请求
→ Provider验证响应满足Consumer需求
→ 部署门禁判断版本能否安全组合

Contract不是完整业务测试。它证明请求响应形状、字段和交互满足约定,不证明Provider内部计算正确,也不证明两个服务在生产配置下完整工作。

Schema兼容不等于语义兼容

Provider新增Optional字段通常兼容;删除Consumer正在读取的字段会破坏Contract。更隐蔽的变化包括:字段仍为String,但从UTC时间改为本地时间;状态码含义变化;排序从稳定变为随机。Contract中应表达Consumer实际依赖的语义,避免约束无关字段。

8. MCP Contract Test

MCP边界至少验证:

  • 支持的Protocol Version和Capability;
  • tools/list中名称、Description、inputSchema、outputSchema;
  • tools/call成功Result、isError和JSON-RPC Error;
  • Malformed、Unknown Tool、Missing Scope、Approval Required;
  • stdio换行Framing与stdout纯协议;
  • Streamable HTTP的Content-Type、版本Header、SSE消息边界;
  • Result大小、Resource Link、脱敏和Trace字段。

Tool Schema可生成Hash并放进Contract Artifact。Server升级Schema时,Consumer CI用真实Server验证旧Case;Host也要测试未知Tool和新增字段不会导致崩溃。

Agent辅助Contract Testing

Agent可以读取OpenAPI、Proto、MCP Tool Schema和历史Trace,生成候选边界Case、识别Consumer真正读取的字段、解释Breaking Change。但生成结果必须经过:

Schema校验
→ 人工确认业务语义
→ Provider真实验证
→ 固化为版本化Contract

不能仅依据模型对文档的分析结果判定兼容性,最终结论必须由真实Provider验证和版本化Contract支持。

9. Fixture设计

Fixture应最小、显式、可组合:

Tenant A
  User admin
  Incident running
  Tool approval pending
Tenant B
  不可见A的数据

Builder提供默认值,Case只覆盖关心字段。Fixture必须版本化;时间、ID和Hash应稳定;涉及PII使用合成数据。测试失败时保留必要日志和Container状态,成功后清理。

10. Testcontainers与CI

Container化依赖让本地和CI运行相近版本。注意:

  • 固定Major/Minor镜像,不用latest;
  • 等待Readiness,不是端口刚开就开测;
  • 容器、网络和Volume有唯一名称;
  • 并行Case避免共享Topic、DB和Consumer Group;
  • 缓存镜像但不缓存脏数据;
  • CI失败上传日志、Schema、Offsets和Trace。

11. Agent系统的真实集成层

可把模型保持Fake,先验证真实基础设施:

Fake Model固定选择 query_logs
→ Real MCP Client
→ Real Log MCP Server
→ Test Loki / HTTP Stub
→ Real Trace Store

再单独用真实模型连接Fake Tool评测Tool Selection。把“模型变量”和“基础设施变量”拆开,失败时才知道该找Prompt、协议还是数据库。

高风险Tool还要验证Policy与Approval是Server-side Enforcement,不是Prompt里一句“请谨慎”。

12. 常见反模式

  • 所有服务一起启动,任何失败都叫Integration Failed;
  • 用H2/SQLite代替PostgreSQL却断言数据库兼容;
  • 测试直接打生产第三方API;
  • Fixture跨Case共享并依赖顺序;
  • Retry把真实失败刷成绿色;
  • Mock契约从不向真实Provider验证;
  • 清理失败后污染后续Case。

13. 面试速答

集成测试和E2E区别

集成测试通常聚焦一个或少数边界,允许内部控制和替身;E2E从外部入口走完整系统与关键旅程。

Contract Test能替代E2E吗

不能。它减少跨服务接口不兼容,但不验证完整部署、路由、配置和用户旅程。

Agent工具为什么要做Contract Test

模型和Host依赖Tool Schema、错误类型及结构化Result。契约漂移可能表现为工具选择或参数生成质量下降,实际原因是协议发生了不兼容变化。

参考