抓包两小时,脚本写一天:接口自动化的“最后一公里”,到底卡在哪?

API Capture & YAML Exporter 源码与实战系列 · 第一篇

抓到请求,不等于跑通场景。接口自动化真正难的,是把一次操作的静态记录,变成能够反复执行的业务链路。

在测试行业摸爬滚打十多年,我见过不少团队在接口自动化上,经历过同一条曲线:满怀期待地起步,轰轰烈烈地写用例,最后悄无声息地停更。

每次做技术分享,我都会问两个问题:

"你们写了多少接口自动化用例?"

"其中有多少,能在 CI/CD 里持续、稳定地跑起来?"

第一个问题,大家通常答得很快。第二个问题,往往会让现场安静几秒。

接着,就是一些熟悉的回答:

"最开始写了上百条,业务一改,就不敢动了。"

"单接口都能跑,一串成下单、支付这样的完整链路,就到处报错。"

"工具大家都会点,但要维护一堆提取表达式和变量,推进起来还是很难。"

这些问题当然不只有一个原因。环境不稳定、测试数据难准备、断言设计不合理,都可能拖垮自动化。

但在从"抓到请求"走向"跑通场景"的过程中,有一个障碍尤其常见,也尤其消耗耐心:上下游接口的参数关联,几乎全靠手工。

也正是为了解决这个问题,我主导开发了 Chrome 扩展 API Capture & YAML Exporter。

这组文章,会结合插件的实现思路和关键代码,聊聊如何缩短从浏览器操作到接口自动化用例之间的距离。

第一篇,我们先把问题讲透:为什么请求都抓到了,脚本还是写不完?

01 请求抓到了,为什么场景还是跑不起来?

先看一个常见的电商下单流程。

| 业务步骤 | 需要传入的数据 | 需要留给后续步骤的数据 | | --- | --- | --- | | 登录 | 账号、密码等登录信息 | token | | 查询商品 | 查询条件,以及接口要求的身份凭证 | sku_id 、price | | 创建订单 | sku_id 、购买数量,以及接口要求的身份凭证 | order_id | | 支付订单 | order_id 、token 等支付所需参数 | 支付结果 |

在浏览器里,这套流程可能只需要点几下。

但把请求从 Charles 或浏览器开发者工具中抓出来,再整理到 Postman、JMeter 或自动化框架里,事情就没那么简单了。

抓包记录里的 token,是这一次登录生成的;order_id,是这一次创建订单返回的。直接照搬这些值,复现的是"刚才那次操作",而不是"一条可以重新执行的业务流程"。

下一次运行,登录态可能失效,商品数据可能变化,订单也可能早已支付。

为了让场景重新跑通,你通常还要做三件事。

第一,手动提取。 在上游响应里找到字段,写 JSONPath 或其他提取规则,例如 $.data.order_id。

第二,手动替换。 把下游请求中写死的值,替换成对应工具或框架支持的变量表达式,例如 {{order_id}}。

第三,持续维护。 一旦响应结构、字段位置或业务流程变化,就要重新检查提取规则和引用关系。

单条链路只有几个接口时,这些工作似乎还可以接受。可当几十条业务场景交织在一起,同一个请求里出现多个 id,Header、Query、Body 又各有一套参数时,维护成本就会迅速放大。

最麻烦的,往往不是写出一个提取表达式,而是不断确认:

这个值,到底来自哪一步?下一次运行时,又该从哪里重新拿到?

抓包留下的是一次操作的静态快照,自动化需要的却是一套可以重复执行的动态关系。

这,就是接口自动化"最后一公里"的一个关键断点。

02 换个思路:抓包时,能不能顺手把依赖也找出来?

传统的工作方式是:先抓请求,再由测试人员逐个分析依赖、编写提取规则、替换变量。

我想把其中一部分机械劳动前移。

既然工具已经看到了前一个接口的响应,也看到了后一个接口的请求,那么它能不能主动发现:

"这次请求里的 order_id,很可能就是前一步返回的那个值。"

这也是 API Capture & YAML Exporter 的核心思路:

不只记录请求,还要帮助识别请求之间的数据依赖,为生成可维护的自动化用例打基础。

它并不是要替代 Charles 的全部能力,也不是试图靠一次录制完成所有测试设计。它聚焦的是一个更具体的问题:让浏览器里的真实业务操作,更容易被整理成自动化场景。

要做到这一点,需要先解决三个环节:抓得到、筛得准、关联得上。

03 第一步:减少配置成本,让浏览器直接成为采集入口

不再为浏览器 HTTPS 抓包额外配置代理证书

使用代理式抓包工具分析 HTTPS 流量时,往往需要配置代理和信任证书。换一台电脑、换一个环境,可能又要重新操作一遍;涉及 App 或证书固定等场景时,情况还会更复杂。

而这个插件的目标场景,是 Chrome 标签页内的业务操作。

既然请求本来就发生在浏览器里,就可以借助 Chrome 提供的调试能力进行采集,而不必额外搭建 HTTPS 中间人代理。

在 manifest.json 中,相关权限配置如下:

bash 复制代码
{
  "manifest_version": 3,
  "permissions": ["debugger", "storage", "sidePanel", "tabs"],
  "host_permissions": ["<all_urls>"]
}

以上为权限配置节选,并非完整清单;实际发布时,应根据功能需要评估并尽量缩小权限范围。

在后台逻辑中,通过 chrome.debugger 连接目标标签页,再启用 Chrome DevTools Protocol(CDP)的 Network 域:

bash 复制代码
// background.js:核心流程示意,省略错误处理与状态管理
chrome.debugger.attach({ tabId }, "1.3", () => {
  chrome.debugger.sendCommand({ tabId }, "Network.enable");
});

chrome.debugger.onEvent.addListener((source, method, params) => {
  if (source.tabId !== tabId) return;

  if (method === "Network.requestWillBeSent") {
    // 记录请求 URL、方法、请求头、请求体等信息
  } else if (method === "Network.responseReceived") {
    // 记录响应状态、响应头、资源类型等元信息
  } else if (method === "Network.loadingFinished") {
    // 请求完成后,按需调用 Network.getResponseBody 获取响应体
  }
});

这里有一个容易被忽略的技术细节:收到 Network.responseReceived,不等于已经拿到了响应体。

响应体通常需要在加载完成后,通过 Network.getResponseBody 单独获取;还要处理读取失败、Base64 编码,以及请求已结束但内容不可用等情况。

相比之下,chrome.webRequest 适合观察请求生命周期和部分元信息,但并不直接提供读取响应体的能力。对于需要分析响应 JSON 的关联功能来说,这个差别很关键。

这条路线的价值是:在支持调试的浏览器页面中,无需额外安装代理证书,就能围绕当前标签页采集业务请求。

当然,它也有边界:调试器连接可能中断,部分页面受限,DevTools 或其他调试会话可能影响采集;页面中的部分子目标还可能需要额外处理。它不是覆盖所有浏览器、所有 App、所有网络流量的通用抓包方案。

04 第二步:先过滤噪音,再谈业务场景

抓得多,不代表抓得有用

打开一个页面,网络面板里可能瞬间出现几十甚至上百条请求:脚本、样式、图片、字体、埋点、监控......真正需要整理成测试用例的业务 API,反而被淹没了。

如果这些请求全部进入后续处理,不仅阅读困难,还会增加关联误判的机会。

所以,在我的设计里,过滤不是附属功能,而是业务整理的入口。

在 options.js 中,多环境配置围绕 hostFilter、featureRule 和 subdirRule 展开,分别服务于域名筛选和路径归类。

先排除明显无关的资源。 结合资源类型与 URL 特征,过滤脚本、样式、图片等请求。仅凭 .js、.png 这样的后缀判断并不总是可靠,因此规则需要可调整,也要避免误删真正的业务接口。

再限定业务域名。 例如只保留 api.mycompany.com 下的请求,减少第三方统计、广告和监控流量的干扰。Host 匹配应基于解析后的主机名,而不是简单判断 URL 是否包含某段字符串。

最后按路径规则归类。 对于 /api/order/create 这样的接口,可以通过配置的正则规则提取 order,将其归入订单模块,为后续组织 YAML 文件和目录做准备。

这里的"懂业务",并不是工具凭空理解了业务含义,而是让团队能够把自己的域名和路径约定,转化为可复用的整理规则。

先把数据范围收紧,后面的关联才更容易做准。

05 第三步:把写死的值,变成可追溯的引用关系

核心思路:响应值索引 + 后续请求匹配

这是整个插件最想解决的问题。

假设创建订单接口返回:

bash 复制代码
{
  "data": {
    "order_id": "ORD_20261025_001"
  }
}

随后,支付请求中出现:

bash 复制代码
{
  "order_id": "ORD_20261025_001",
  "payment_method": "balance"
}

两处数据的值相同,而且存在明确的先后关系。工具就可以提出一个关联候选:

支付请求的 order_id,引用创建订单响应中的 $.data.order_id。

在 sidebar.js 的关联思路中,这个过程可以拆成两个动作。

先记录:这个值从哪里来?

上游响应被采集并解析后,遍历 JSON 中的字段,记录字段值、数据类型、来源步骤和 JSON 路径等信息。

id、token、order_id 等字段可以作为优先关注的线索,但字段名本身不能证明依赖关系。例如 code 既可能是业务编码,也可能只是几乎每个接口都会返回的状态码。

这些信息构成一次录制会话内的上下文池。它不应跨账号、跨环境无限累积,否则旧数据很容易干扰新场景。

再匹配:这个值是否被后续请求使用?

检查后续请求的 Header、Query 和 Body,将其中的参数值与先前响应建立的索引进行比对。如果命中,就记录引用候选。

下面是用于说明原理的简化代码,不代表完整实现:

bash 复制代码
// sidebar.js:关联候选发现示意
// contextPool 仅包含当前场景中、此前已采集完成的响应字段。
// flattenRequest 将 Header、Query、Body 展平成"参数路径 → 值"。
function findReferences(currentRequest, contextPool) {
  const references = [];

  for (const [targetParam, value] of Object.entries(
    flattenRequest(currentRequest)
  )) {
    // 跳过容易产生无意义匹配的值
    if (value === null || value === "" || typeof value === "boolean") {
      continue;
    }

    const candidates = contextPool.filter(
      (item) =>
        typeof item.value === typeof value &&
        item.value === value
    );

    if (candidates.length > 0) {
      references.push({
        targetParam,
        candidates: candidates.map((source) => ({
          sourceStep: source.stepName,
          sourcePath: source.jsonPath
        }))
      });
    }
  }

  return references;
}

这段逻辑的作用不是证明业务依赖,而是把原本需要人逐个查找的线索,提前整理出来。

确认关联后,导出阶段就有了生成"上游提取规则 + 下游变量引用"所需的信息。具体的变量语法和提取格式,则要适配目标自动化框架。

必须讲清楚:值相同,不一定就是依赖

"两个值一样,就自动关联"适合解释原理,但不足以支撑可靠的工程实现。

例如,多个响应都可能出现 0、1、success,不同接口也可能碰巧返回相同的数字 ID。如果只取第一个匹配项,很容易把用例关联到错误的来源。

反过来,真正的依赖也未必表现为完全相等:响应中的 token,进入请求头后可能变成 Bearer <token>;JSON 里的数字,放进 Query 后通常会变成字符串;有些值还会被拼接、编码或加工后再使用。

因此,要让关联结果更可靠,还需要逐步补齐这些判断:

  • 限制来源范围。

    只在当前场景中具有合理先后关系的步骤里寻找来源,结合字段语义、路径和时间距离排序。

  • 过滤低信息量值。

    对常见状态码、布尔值、空值和短小通用值,跳过匹配或降低可信度。

  • 保留冲突与不确定性。

    多个来源同时命中时,展示候选供用户确认,而不是悄悄选一个。

  • 谨慎处理转换。

    对类型转换、前缀拼接和编码等情况使用明确规则,不能为了提高命中率而随意模糊匹配。

这些是从"找到相同值"走向"可靠关联"必须解决的工程问题,不能因为核心流程已经跑通,就默认它们也都解决了。

另外,网络事件是异步到达的。下游请求出现时,上游响应体未必已读取完成,因此关联也不能只依赖请求发出瞬间的一次扫描;必要时需要在数据齐备后补算。

我更希望它成为一个能给出依据的关联助手,而不是一个不允许质疑的黑盒。

06 少做机械劳动,不等于少做测试设计

自动识别参数关联,解决的是"数据从哪里来、要传到哪里去"的一部分工作。

它不能自动回答:这笔订单算得对不对?重复支付有没有被拦住?库存不足时是否正确回滚?用户能否访问不属于自己的订单?

这些仍然需要测试工程师理解业务、设计断言、构造异常场景。

同样,录制出来的请求也不能不经检查就直接提交到仓库。Token、Cookie、账号信息和个人数据需要脱敏或替换;测试环境、初始化数据和清理策略也需要单独管理。

自动化关联的价值,不是"一键生成完美测试",而是减少重复劳动,让人有更多时间做真正影响测试质量的判断。

07 结语:从"记录请求",走向"整理关系"

回头看这套思路,重点并不只是换一种方式抓包,而是把三个环节接起来:

通过浏览器原生调试能力降低采集门槛;通过域名和路径规则减少噪音、组织业务;再通过上下文匹配,把写死的数据整理成可追溯的关联候选。

API Capture & YAML Exporter 的第一版,已经跑通了这些核心流程。但核心逻辑跑通,只是起点。误关联控制、复杂参数转换、采集稳定性,以及导出后的实际执行效果,都需要继续在真实场景中打磨。

我希望它最终缩短的,是这段最耗费耐心的距离:

从"我已经在浏览器里操作了一遍",到"我拥有一条可以持续维护、重复执行的自动化场景"。

下一篇,我们先把采集这一层拆开,聊聊《不配代理证书,如何打造一款面向业务场景的 Chrome 抓包插件?》,重点看 background.js 的调试会话与请求生命周期管理,以及 options.html 中的规则配置与正则测试设计。

至于为什么选择 YAML、关联关系如何落到文件里、又怎样对接执行框架,我们会在后续的导出篇继续展开。


你在接口自动化中,遇到过最难维护的参数关联是什么?

是层层嵌套的 ID、不断变化的 Token,还是必须经过多个接口加工才能得到的参数?欢迎在评论区留下你的场景,注意隐去敏感信息。我会挑选有代表性的问题,在后续文章中结合实现思路拆解。

如果这篇文章对你有帮助,也欢迎转给正在被参数关联困住的同事。

相关推荐
梦醒江南2 小时前
别让模型每次 CI 都重跑:混合式 e2e 怎么拆步骤
开源·agent·测试
狂师6 小时前
夯爆了!分享几个我在用的,基于业务场景接口串联测试Skill !
人工智能·后端·测试
狂师1 天前
2026年AI安全测试工具大全,14款大模型与Agent安全工具一次配齐
安全·agent·测试
匠测AI说2 天前
AI for Testing 提效实战 · 执行自动化(六):轻量 Mock 编排实战——WireMock 手工落地 + AI 加速
人工智能·测试
KKWOO4 天前
100 万次回归测试,究竟覆盖了多少种真实业务逻辑?
测试
ClouGence4 天前
敏捷开发如何做好测试?一篇讲清流程与实践
测试·敏捷开发
linoWu4 天前
1.接口自动化框架最小demo
测试
kevinccbsg4 天前
AI Agent 说测试都通过了?看着它跑一遍
测试
安替-AnTi4 天前
不用 root:我用 Tailscale 打通了云手机的 ADB 通道
adb·测试·调试·云手机·claude code