第三章 Mock回放基本原理

这一章我们聊聊最核心的东西——Mock回放究竟是怎么工作的。说白了,就是先录一段真实的网络交互,然后在测试时原样放出来。听起来简单吧?但里面坑不少,我当年刚接触时就被匹配策略坑过好几次。

录制-回放模式

先看两种基本模式:

  • 录制模式:启动一个代理,把真实的前台请求和后台响应原封不动地保存下来。
  • 回放模式:关闭真实后端,让请求过来时从已录制的数据里找匹配的响应返回。

我常用的一招:先打开录制跑一遍完整的业务流,再切换到回放重复跑。这个过程中,前台的代码完全不需要改——只需要改个配置开关,连启动命令都不用变。

💡 小窍门:录制时最好把重要的请求记录成“黄金数据集”。我习惯每次发版前重新录一遍,保证数据是最新的。

HTTP请求/响应捕获

捕获的本质就是中间人代理。咱们看看它抓住了什么:

捕获内容说明常见坑
请求方法 + URL GET /api/user/profile URL里的动态参数容易导致匹配失败
请求头 Cookie、Authorization等 过期后回放可能不匹配
请求体 JSON / Form / 二进制 大文件或流式请求要特殊处理
响应状态码 200 / 404 / 500 注意状态码也要匹配
响应头 + 响应体 Content-Type, 实际返回数据 Content-Encoding如果是gzip,存储时需解压

曾经有个项目,前端一直报跨域错,查了半天发现录制时响应头里没存Access-Control-Allow-Origin,回放时浏览器给拦截了。从此我再也不敢省略响应头。

匹配策略

匹配是回放的心脏。策略不对,要么死活找不到匹配,要么错配数据导致测试失败。

精确匹配

最简单粗暴:请求的所有关键字段必须完全一致。包括URL参数顺序、请求体里的空格。我在项目里遇到过因为JSON里多了一个换行符导致匹配失败的,排查到凌晨两点。

模糊匹配

允许用通配符或正则匹配动态部分。比如时间戳、随机数、会话ID。我现在做匹配配置时,会给每个动态字段加一个{ignore}标记,省心很多。

⚠️ 注意:模糊匹配容易造成误匹配。我曾经有一回把/trade/order/123误配给了/trade/order/456,导致测试断言全乱套。后来我强制要求匹配时至少锁定method和URL路径,再配合模糊参数。

优先级与回退

多匹配器并存时,一般按精确->模糊->默认响应的顺序查找。我习惯给每个接口配一个“兜底响应”,避免匹配不到直接抛异常。

// 伪代码示意:匹配优先队列
matchingPipeline:
  - type: exact
  - type: regex
    pattern: "/api/v\d+/orders/(\\d+)"
  - type: fallback
    responseFile: "default_ok.json"

数据存储模型

录下来的数据怎么放?常见有三种模型:

模型特点适用场景
文件树每个请求一个YAML/JSON文件,按目录组织小项目,手动维护
数据库表用SQLite或内存DB存储请求体与响应体中等规模,支持索引查询
键值对映射用请求指纹(如SHA256)做key,响应做value高性能,适合大量录制数据

我个人比较偏爱键值对模型。原因很简单:查找快,还能支持增量录制。不过注意指纹碰撞——我曾在Key里加入URL和method,但忽略了请求体顺序,导致两个请求被当成一个。

存储最佳实践:每个录制条目至少包含:

  • request.key:由method + URL + 请求头摘要组成
  • request.bodyHash:请求体哈希,用于区分POST请求
  • response.status、response.headers、response.body
  • metadata.createdAt:录制时间,方便过期清理

嗯,这张图能帮你快速把握整体流程:

Mock回放核心流程 ① 录制阶段 ② 捕获HTTP请求/响应 ③ 存储数据模型 存储内容: • 请求指纹 (key) • 响应体 + 状态码 • 元数据(时间等) ④ 回放阶段 ⑤ 匹配策略 精确匹配 模糊匹配 默认回退响应 ⑥ 返回录制响应

最后说说数据存储里的一个细节:索引结构。我用的模型大致长这样:

{
  "fingerprint": "GET:/api/user:hash1234",
  "request": {
    "method": "GET",
    "path": "/api/user",
    "query": "id=42",
    "headers": {"Authorization": "Bearer ***"},
    "body": null
  },
  "response": {
    "status": 200,
    "headers": {"Content-Type": "application/json"},
    "body": "{\"name\":\"Alice\"}"
  },
  "meta": {
    "recordedAt": "2025-02-18T10:00:00Z",
    "ttl": 86400
  }
}

曾经我犯过一个错:忘了给录制数据加过期时间,结果压测时回放了三个月前的老数据,接口完全变了。后来我强制每个录制条目都带ttl字段,超时就自动重录。嗯,这些细节积累下来,能让你的Mock回放真正靠谱起来。