背景
我做了 8 年测试开发,从单体到微服务、从 Python 到 Java,带过的自动化接口用例从几百条到上万条。有一件事我至今没找到"标准答案":接口自动化用例里,下游依赖(RPC / HTTP / MQ / DB)到底该 mock 到哪一层?
这个问题表面是"技术选型",实际是"测试目标"之争——你要验的是"我的逻辑对不对",还是"整条链路通不通",还是"数据一致性保没保住"。目标不同,mock 边界就完全不同。
我见过的四种典型做法(以及各自翻车现场)
做法 A:全 mock 到 RPC 边界 所有下游(包括内部微服务)全部用 WireMock / Mockito 拦截,只测本服务。
- 优点:用例快、稳、可并行,CI 上 200 条用例 3 分钟跑完。
- 我踩的坑:2022 年某次上线,本服务逻辑没问题,但下游接口把字段名从
amount改成了amountV2,全 mock 的用例一条都没挂,直接上了生产。事后复盘才意识到:mock 掉的是"契约",你验的根本不是真实交互。
做法 B:全真实链路(只 mock 第三方外部服务) 内部微服务全部走真实调用,只 mock 支付宝 / 微信 / 短信网关这类外部依赖。
- 优点:最接近生产,能发现契约变更、路由配置错误。
- 我踩的坑:测试环境 5 个服务共享一套 DB,A 服务的用例造的数据被 B 服务的用例删了,flaky 率一度到 15%。更惨的是某次 MQ 消费顺序依赖时间戳,两个用例并发跑就互相污染,排查了一整天。
做法 C:mock 到 DB 层(H2 / Testcontainers 替身) DB 用内存库或容器替身,RPC 走真实调用。
- 优点:数据隔离好,每条用例自带 setup/teardown。
- 我踩的坑:H2 和 MySQL 的 SQL 方言差异(
ON DUPLICATE KEY UPDATEvsMERGE INTO、LIMIT语法、字符集排序规则),本地全绿、上 MySQL 就挂。后来改 Testcontainers 好多了,但 CI 冷启动 +30s 让研发同学开始抱怨。
做法 D:不 mock,靠"造数 + 环境隔离" 每条用例自己造数据、用独立 tenant_id / namespace 隔离,下游全走真实。
- 优点:最"端到端",能发现数据层面的问题。
- 我踩的坑:造数脚本本身就是个大坑。造 1000 条订单流水要 40 秒,一条用例还没跑完造数就占了 80% 时间。而且造数逻辑和业务逻辑耦合,业务字段一改造数脚本就崩。
我的倾向:按"信任边界"分层,而不是按"技术栈"分层
我现在的实践是:
- 本服务内部逻辑:不 mock,真实执行。这是你"自己的代码",必须验。
- 内部微服务间调用:走真实 RPC,但用独立 tenant / namespace 做数据隔离。理由:契约变更是最常见的线上事故来源,mock 会把这个盲区藏住。
- 外部第三方(支付 / 短信 / 物流):必须 mock,用 contract 文件(比如 Pact)锁住交互契约。理由:你控制不了对方,也不该让 CI 依赖对方的可用性。
- DB:生产级测试用 Testcontainers(真实 MySQL 容器),单元测试用 H2 但必须加方言兼容断言。理由:DB 方言差异是"本地绿、生产红"的高频根因。
一句话总结:mock 的边界 = 你"不信任"的边界。你信任自己的代码、信任团队的契约,就不 mock;你不信任外部系统、不信任共享环境的数据隔离,就 mock。
想听大家聊聊
- 你们团队的 mock 边界是怎么划的?是"按服务"还是"按测试目标(单元 / 集成 / E2E)"?
- 有没有遇到过"mock 掉之后用例全绿、上线直接炸"的事故?当时怎么发现、怎么补的?
- Testcontainers 在 CI 里的冷启动成本你们怎么压的?有没有用 image cache / sidecar 常驻容器的?
- 契约测试(Pact / Spring Cloud Contract)你们真用上了吗?还是最后都退化成"手工对一下接口文档"?
欢迎贴你们的实践和踩坑清单,我这边先把我的"避坑清单"整理出来放评论区:
- 别用 H2 测
ON DUPLICATE KEY UPDATE - mock 外部服务时别只 mock 200,429 / 500 / 超时也要有用例
- Testcontainers 的 DB 端口别写死,用
getMappedPort() - 造数脚本和业务代码分仓,别放同一个 repo 的 test 目录里"顺手改"
0赞
0收藏
0楼层