本地开发环境里,依赖服务到底该用真环境、mock 还是契约测试?
帖子正文与楼层回复。
背景
我 10 年里换过 4 个团队,每个团队对"本地开发怎么跑起来"这件事的答案都不一样。有的是"本地起全套 docker-compose,连真库、连真 Kafka、连真 Redis";有的是"下游全部 mock,只测自己这一层";还有的是"契约测试 + testcontainers,CI 里跑真依赖,本地全 mock"。三种姿势各有各的坑,也各有各的爽点。
最近在做一个跨 6 个服务的订单链路改造,本地联调的痛点又集中爆发了:A 服务改了个字段名,B 服务本地跑着旧版,联调时才发现 400;C 服务依赖的 Kafka topic 本地没起,mock 的 consumer 和真 consumer 行为差了一截,到 staging 才炸。我想把这个问题摊开聊聊。
几种常见做法(各自的坑)
-
全真环境(docker-compose 起全套):最"真实",但起一套 6 服务 + MySQL + Kafka + Redis 的 compose 要 4~8 分钟,每次改完重启一轮开发节奏就断了。而且本地资源有限,Kafka 单节点和 3 节点集群的 rebalance 行为不一样,mock 出来的问题到线上才暴露。
-
全 mock(WireMock / 手写 stub):快,但"mock 的边界"经常和真实行为有 gap。最典型的是:mock 返回 200 + 固定 JSON,但真实服务在超时、部分失败、重试场景下的响应完全不同。我见过一个团队 mock 了 20 多个下游,结果联调时 30% 的 case 是"mock 没覆盖到的分支"。
-
契约测试 + testcontainers(CI 里跑真依赖,本地轻 mock):理论上最优雅——开发者本地用轻量 stub 快速迭代,CI 里用 testcontainers 起真 MySQL/Kafka 跑契约测试,保证"接口没变"。但落地时我发现两个坑:一是 testcontainers 的 Kafka 集群和 3 节点生产集群在 partition 分配、ISR 行为上有差异,某些 edge case 复现不了;二是契约测试的维护成本——6 个服务两两之间的契约有 15+ 条,每次接口变更都要同步改契约文件,团队里没人愿意碰。
我的倾向
我倾向"本地轻 mock + CI 契约测试 + 关键路径真环境"的混合策略,具体是:
- 本地:只 mock 掉"慢的、有状态的、跨团队的"依赖(比如支付网关、短信服务),自己团队内的服务用 docker-compose 起真服务,哪怕多花 2 分钟。理由:团队内服务的接口变更是高频的,mock 的 gap 代价大于启动时间。
- CI:用 testcontainers 起 MySQL + Kafka,跑集成测试(不是单元测试),重点覆盖"超时、重试、幂等"这三类场景。Kafka 用 testcontainers 的 KRaft 单节点,虽然和 3 节点有差异,但 90% 的集成 case 够用。
- staging:保留一个"全真环境",但不是给开发日常用的,而是给"发布前最后一轮端到端验证"用的。关键路径(下单 → 扣库存 → 发通知 → 对账)必须在这个环境跑通。
核心原则:mock 的边界 = 团队边界。团队内的依赖用真的,团队外的依赖用 mock + 契约。
想听大家聊聊
- 你们团队本地开发时,依赖服务到底怎么处理的?有没有"mock 了 3 个月,上线第一天就炸"的经历?
- testcontainers 的 Kafka 单节点和 3 节点集群,你们在实际项目中遇到过哪些行为差异?
- 契约测试的维护成本你们怎么控的?有没有什么工具或流程让"改接口"和"改契约"变成一件事?
- 你们团队有没有"本地起全套太慢,干脆不联调,直接推 staging"的"懒法"?效果怎么样?
期待不同规模、不同技术栈的团队分享各自的姿势,尤其是 2 人小团队和 50 人大团队的差异——我猜答案会完全不同。