单测跑一次要连测试库、还要等第三方支付回调,CI 上十次有三次超时标红。同事说加 mock,加完测试是快了,可上线还是漏了个字段。

mock 是替换掉外部依赖,返回安排好的结果。它验证的是「依赖给出这种响应时你的代码怎么处理」,不是依赖本身对不对。

该 mock 什么

  • 第三方 HTTP 接口、支付回调
  • 数据库、消息队列
  • 时间和随机数,不然测不出边界情况
  • 文件系统和环境变量

不该 mock 的是被测逻辑自己的分支。mock 太多,测试就变成复述实现,改一行代码要改十个测试,最后没人愿意维护。

Python 里怎么写

with patch(target, return_value=FAKE_OK) as m:

打桩路径要打在「被使用的地方」,不是定义的地方。支付模块里 import 了 charge,就在引用它的那个命名空间上打,打在原函数上不生效,这是 patch 最常见的失败原因。

HTTP 用 responses 库按 URL 匹配,比 patch 整个函数更接近真实;要断言调用参数,用 assert_called_once_with,别只断言返回值。

别让 mock 掩盖问题

mock 的返回结构一旦和真实接口不一致,测试全绿,线上就报 KeyError。补一层契约测试,拿 OpenAPI schema 校验 mock 数据,或者定期跑一次连真实沙箱的集成测试。

几个小细节值得写进规范:mock 时间用 freezegun 冻结,别在断言里写当前年份,跨年就挂;随机数固定种子,否则偶发失败最费时间;CI 里把桩的返回值打进日志,红了能一眼看出是桩错了还是代码错了。

分界线很简单:一个函数要 mock 五个依赖才测得动,先想是不是该拆,而不是继续加 patch。