HAR一键生成浏览器扩展:从抓包到禅道提Bug全流程自动化 1. 测试提 Bug 的隐藏成本真的比你想的高干测试这么多年我一直觉得最浪费时间的环节不是执行用例而是Bug 已经在你眼前了证据却还没到手。你刚在一个页面上点出来一个 500顺手按 F12 准备导出 HAR一抬头发现接口响应已经刷过去了再一看 Network 面板里什么都没留下——因为你还没勾上 Preserve log。于是只能复现一次可复现偏偏又不稳定第二次怎么点都不报错了。这种场景凡是做过 Web 测试的应该都懂。传统提 Bug 的完整链路是发现问题 → 打开开发者工具 → 勾选 Preserve log → 复现问题 → 右键导出 HAR → 登录禅道 → 新建 Bug → 填标题、模块、严重程度、影响版本 → 上传 HAR 附件 → 写复现步骤 → 提交。顺利的话三到五分钟不顺利的话光“影响版本”这个下拉框就能卡你十分钟——版本列表里没有当前跑的版本你选谁都不对选“其他”又会被开发随手打回。更糟的是环境不稳定开发那边已经把旧包覆盖了你想补抓一个旧版本的 HAR 都无路可走。我这次想聊的就是我们团队内部打磨的一个小工具HAR 一键生成 Skill。它本质上是一个跑在浏览器里的扩展核心动作是捕获当前页面所有网络请求拼装成标准 HAR 文件同时自动读取当前页面的版本号在禅道里新建用例或提交 Bug 时能跳过版本下拉框的等待和匹配直接把证据和现场信息一次性带过去。适合所有天天和 Web 项目打交道的测试工程师项目越复杂、版本迭代越快它的价值越明显。1.1 传统流程到底浪费在哪先掰开算一笔账。假设每天处理 5 个 Bug每个 Bug 光是“抓包 填单”就要花 10 分钟那一天就是 50 分钟一个月按 20 个工作日算整整浪费 1000 分钟。这还没算“复现不出来被迫重新跑一遍用例”的时间。很多团队对 Bug 单的要求是“必须附带 HAR”因为开发需要看请求参数、响应头和耗时数据。可如果你恰好在发现问题之后没有第一时间抓包后面补起来就是地狱难度。另一个浪费点在于“版本信息靠人肉记忆”。禅道里的“影响版本”字段理论上应该填当前被测版本。可实际执行测试时你经常同时开着两三个环境一个联调环境、一个测试环境、一个预发布环境页面长得一模一样只是 URL 带的后缀不同。等你切到禅道去填单时早就忘了刚才点的是哪个版本。Bug 单上的版本信息一错开发就得重新确认一来一回又是一轮沟通成本。1.2 Skill 到底解决什么问题这个 Skill 的本质不是“自动帮你点按钮”而是把“发现 Bug → 留下证据 → 关联版本 → 提交禅道”这条链路压缩成一次点击。它的工作方式大致这样你在被测页面上看到一个异常点一下悬浮按钮扩展会从浏览器后台拿回这个页面近期所有请求数据、拼成 HAR再自动打开禅道的新建 Bug 页面把标题、复现步骤、影响版本、HAR 附件全部填好。你只需要检查一遍、点提交。“不再受版本限制”这句话我理解包含两层意思。第一HAR 是现场抓的跟你跑的是哪个分支、哪个镜像、哪次构建都无关只要页面还开着你就能拿到当下的完整请求证据不需要为了复现而去部署旧版本第二版本字段不再要求下拉框里刚好有选项工具会自动从页面 URL、meta 标签或页脚文字里提取当前版本号能匹配上就自动选匹配不上也会写进 Bug 描述里确保信息不丢失、提交不阻塞。2. 整体设计这枚 Skill 是怎么搭出来的工具做出来看着简单但设计过程中我踩了不少坑。先说结论整体架构分三层——浏览器扩展负责采集和注入中间的逻辑层负责 HAR 构建和版本识别最后是禅道动作层负责建用例和提 Bug。每一层都要考虑浏览器的限制和禅道页面本身的交互特点。2.1 为什么非要做成浏览器扩展很多同事第一反应是抓包工具不是现成的吗独立抓包工具也能导出 HAR再写个脚本上传到禅道不就行了理论上可行但实际用起来很别扭。独立抓包工具需要额外的代理配置手机和电脑混测时尤其麻烦而且它读不到页面上下文里的版本号、登录态、当前用户信息这些数据在浏览器页面 DOM 里才有。浏览器扩展的优势是“贴地飞行”。它可以注入脚本到页面里读取 URL、meta 标签、全局变量可以通过 chrome.webRequest 系列接口监听网络请求还可以主动打开禅道页面模拟用户交互去填表单。更重要的是它能把“记录请求”和“禅道提交”这两件事真正串成一个动作而不是让你在多个工具之间来回切换。这也是我们内部管它叫 Skill 而不是“插件”的原因——插件强调功能Skill 强调场景动作抓包、转用例、提 Bug 是一条完整的技能链。2.2 三个核心模块的分工模块划分是我觉得这个项目里最值得参考的部分。不需要一开始就做一个大而全的框架只需要把三个能力边界理清楚模块职责关键技术点网络请求采集器收集页面所有 HTTP 请求的信息chrome.webRequest 监听、performance 兜底、请求去重HAR 构建器把采集到的数据转成标准 HAR JSON字段映射、时间戳格式化、响应体截断禅道动作器打开禅道并自动填表提交DOM 自动填充、受控组件事件触发、版本模糊匹配三个模块之间通过浏览器扩展的消息机制串起来。采集器把原始请求数据发给构建器构建器生成 HAR 后存到扩展的本地存储里禅道动作器再从存储里取数据去填表。这样做的好处是每个环节都可以单独调试——比如 HAR 构建器只负责生成文件即使禅道页面改版了也不用动采集逻辑。2.3 版本字段解绑的设计思路版本解绑是这个小工具的灵魂。最开始我们想的方案是直接读出页面 URL 里的版本号比如https://app.example.com/v2.3.1/#/order就提取v2.3.1。但实际项目里版本号经常只出现在登录后的页脚里比如“版本号2024.06.18-release”甚至藏在打包时注入的全局变量里。后来我们定了三条提取路径按优先级依次尝试URL 正则 →meta[nameversion]→ 页脚可见文本。提取到纯文本版本号之后再对禅道版本下拉框里的选项做模糊匹配把带相似前缀的选项自动选中。如果最后还是匹配不上就退而求其次把识别到的版本号写进 Bug 复现步骤里并给“影响版本”字段填一个默认值。这样 Bug 单不会被卡在必填校验上开发拿到单还能从描述里看到准确版本。这一步很重要别为了追求“全自动选中”而把提交动作卡住目标是信息不丢而不是 UI 完美。3. HAR 生成核心拆解数据从哪来、怎么转HAR 这个东西看着是一个 JSON 文件里面有名堂。如果你只是手动从 DevTools 导过一次可能觉得它长得挺标准但等你要自己生成一份能让禅道附件正常展示、开发能打开分析的 HAR就会遇到一堆细节问题。3.1 HAR 结构速览与最小可用模型HAR 的整体结构是一个log对象里面包含version、creator和entries数组。每个entry对应一次完整的 HTTP 请求包含startedDateTime、time、request、response、timings等字段。{ log: { version: 1.2, creator: { name: TestHAR-Skill, version: 1.0.0 }, entries: [ { startedDateTime: 2025-01-12T14:23:11.000Z, time: 234.5, request: { method: GET, url: https://api.example.com/v1/order/list, headers: [], cookies: [], queryString: [], postData: { mimeType: application/json, text: {} } }, response: { status: 500, statusText: Internal Server Error, headers: [], content: { mimeType: application/json, size: 128, text: }, redirectURL: }, timings: { blocked: 10, dns: 5, connect: 30, send: 5, wait: 180, receive: 5 }, cache: {} } ] } }很多开发同学拿到 HAR 后第一件事就是看请求参数、响应状态和耗时。所以我在构建的时候坚持三个原则请求按时间排序、状态码必须真实、响应体尽量截断保存。真实环境里一个响应体动辄几百 KB全塞进 HAR 会导致附件体积爆炸后面在禅道上传时非常痛苦。最小可用模型的意思是字段可以缺但 URL、状态码、请求头、耗时这几个核心字段必须完整。3.2 用 webRequest 捕获请求的几个关键坑在 Chromium 系的扩展里监听网络请求最直接的 API 是chrome.webRequest。但用起来有几个容易踩的坑。第一个坑是 Manifest V3 里webRequest不再支持同步修改请求内容的blocking权限除非企业策略所以别想着在请求发出前改参数我们只需要“看”用onCompleted和onErrorOccurred就够了。第二个坑是监听回调里拿不到响应体。chrome.webRequest只给你响应头、状态码不给你响应内容。想拿响应体得上chrome.debuggerAPI但那玩意儿权限更大、用户会有“调试器已附加”的提示条体验很差。我们的折中方案是默认只记录响应头和大小响应体通过页面的performance接口兜底获取拿不到就留空。开发要看具体返回内容时再配合控制台操作。第三个坑是 Service Worker 生命周期。Manifest V3 的扩展后台是 sw 进程一段时间不活动会休眠可能错过正在发起的请求。这问题没有银弹只能靠“在页面打开时 keepalive”或者把采集逻辑部分放到 content script 里用页面自身的生命周期来兜底。我最终用的是 content script 里的 performance 兜底方案后面细说。3.3 用 Performance API 做数据补全performance.getEntriesByType(resource)这个方法能取到当前页面加载以来所有资源请求的元数据。它和webRequest的区别在于它拿不到所有响应头但能拿到 Duration、TransferSize、RequestStart、ResponseStart 这些时间戳而且它在扩展 content script 里也能访问不受后台休眠影响。实际实现的时候我做了两层融合。以webRequest为主因为它能拿到完整的请求头和部分响应头适合做 HAR 的骨架再用 performance 条目补全startedDateTime、耗时等数据顺便给那些被缓存命中、没触发网络事件的请求补一条记录。两条数据源靠 URL 和时间戳做关联以 URL 去重这样生成出来的 HAR 不会漏掉页面的静态资源请求也不会重复记录同一个接口。这里有个小技巧performance条目里的startTime是相对页面加载开始的时间不是绝对时间。要转成 HAR 里的startedDateTime需要先performance.timeOrigin拿到页面加载的绝对基准时间再相加。踩坑踩过一次之后我对这种“相对时间”的细节格外敏感。3.4 版本号识别的实现策略版本识别这件事我写了一个独立的detectVersion()函数按顺序尝试从location.href里匹配常见版本号模式比如v?[0-9]\.[0-9](\.[0-9])?以及日期版本号2024\.\d\.\d查document.querySelector(meta[nameversion])和meta[nameapp-version]如果上面都没有就扫描页脚区域footer、.version、.release等常见选择器取第一个符合版本号格式的文本。识别出的版本号会先展示在悬浮按钮旁边方便测试人员肉眼确认。识别错了还可以手动改。这个交互细节很重要——自动化识别再准也得留人工确认的口子否则识别错误会把 Bug 带到错误版本上比不识别还坑。4. 禅道自动化建用例、填 Bug、解绑版本工具要落到禅道上才算真正闭环。禅道的表单不算复杂但有几个交互细节非常磨人富文本编辑器、异步加载的下拉框、受控组件。我分别说一下我是怎么处理的。4.1 从 HAR 自动生成 Bug 标题标题是 Bug 单的第一印象不能随便起。自动生成标题的规则我参考了团队历史 Bug 单的高质量样本总结出一个简单模板[页面/模块] 接口 X 返回 YYY导致 Z 问题具体做法是先筛选出 HAR 里状态码非 2xx 的请求按严重程度排序5xx 4xx 超时。取最严重的一个请求从 URL 里提取接口路径片段比如/order/detail状态码直接取Z 问题则从请求 URL 的 query 参数里的message、err_msg关键词去猜猜不到就写“异常”留给你自己改。举个例子如果 HAR 里有一条POST /api/payment/confirm返回 500那么生成的标题就是“【订单模块】接口 /api/payment/confirm 返回 500导致支付确认失败”。如果禅道里已经存在相同标题的 Bug我会在标题尾部加“(已有相似单建议核实是否重复)”防止测试同事因为自动化而疯狂提单污染 Bug 库。4.2 复现步骤和用例草稿怎么自动生成这是我觉得最有价值的部分也是标题里“建用例”的来源。HAR 本身就是一段真实操作的行为记录把它变成用例步骤并不难。我的做法是首先按时间排序所有请求找出状态码异常的那个请求把它之前的三个相邻请求视为“前置操作链路”。然后对每一条请求生成一个步骤描述规则是“操作”加“断言”。比如前面三条请求是打开首页 → 点击支付按钮 → 提交支付表单那么生成的用例步骤就是进入订单确认页点击“确认支付”按钮观察接口/api/payment/confirm的返回值预期返回 200页面展示支付成功实际接口返回 500支付失败。生成好的用例草稿会渲染在一个中间预览面板里你可以在提交前直接编辑也可以一键“创建到禅道用例模块”。禅道用例模块的字段是标题、前置条件、步骤、预期这几个字段在预览面板里都能改。这里我强烈建议保留人工编辑因为纯自动生成的步骤描述难免粗糙测试人员的判断力不能省。4.3 填表单遇到的受控组件与异步下拉框问题禅道是一个存在了很久的开源项目前端历史包袱不少。有些页面用的是老式 jQuery 表单有些模块已经改成了现代框架这就导致“填值”不能只用input.value xxx这条路否则输入框的值看着变了但提交的时候表单拿不到。解决方案是对 input 和 textarea 用setNativeValue方式设置值并派发input和change事件。setNativeValue的核心就是利用原生 setter 绕过框架的取值缓存function setNativeValue(element, value) { const valueSetter Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, value).set; valueSetter.call(element, value); element.dispatchEvent(new Event(input, { bubbles: true })); element.dispatchEvent(new Event(change, { bubbles: true })); }版本下拉框的问题则是“异步加载”。禅道的版本选项是打开页面后通过接口加载的如果你刚打开页面就去选下拉框里还是空的。解决方案是轮询等待先记录当前下拉框选项数量每 300ms 检查一次直到选项数稳定后再执行版本匹配。千万别一上来就 sleep 两秒禅道接口快的时候一半时间都用不上慢的时候 5 秒都等不完。5. 从 0 到 1 完成这枚 Skill 的实操步骤现在进入最实操的部分。我完整记录了一遍搭建流程照抄即可跑通一个最小可用版本。5.1 扩展骨架与权限配置首先创建一个目录里面放manifest.json这是扩展的入口。核心权限如下{ manifest_version: 3, name: HAR-Skill, version: 1.0.0, permissions: [webRequest, activeTab, scripting, storage], host_permissions: [all_urls], background: { service_worker: background.js }, content_scripts: [ { matches: [all_urls], js: [content.js], run_at: document_idle } ], action: { default_popup: popup.html, default_title: HAR-Skill } }说明一下webRequest权限是采集流量的基础host_permissions用all_urls是为了在任意被测站点上都能工作——如果你只测固定域名建议收紧到具体域名减少权限风险storage用来在扩展不同页面之间传递 HAR 数据。5.2 注入悬浮按钮与捕获入口content.js负责在页面上注入一个悬浮按钮。代码很简单但有一个要点按钮要挂在document.documentElement下并且要设置很高的z-index否则容易被页面自己的导航条或弹层盖住。const floatingBtn document.createElement(div); floatingBtn.textContent ; floatingBtn.style.cssText position:fixed;right:24px;bottom:120px;z-index:2147483647;...; document.documentElement.appendChild(floatingBtn); floatingBtn.addEventListener(click, () { chrome.runtime.sendMessage({ type: CAPTURE_HAR_REQUEST }); });按钮点击之后只是向后台发一条消息。真正抓包和构建 HAR 的逻辑放在background.js里因为chrome.webRequest接口只有扩展后台才能用。5.3 background 拼接 HAR 的核心逻辑background.js里维护一个数组requestLog每次onCompleted或onErrorOccurred触发时把请求信息 push 进去。收到CAPTURE_HAR_REQUEST消息时再统一拼装成 HAR。const requestLog []; chrome.webRequest.onCompleted.addListener( (details) { requestLog.push({ url: details.url, method: details.method, statusCode: details.statusCode, statusLine: details.statusLine, requestHeaders: details.requestHeaders, responseHeaders: details.responseHeaders, time: details.timeStamp }); }, { urls: [all_urls], types: [xmlhttprequest, script, stylesheet, image] } ); chrome.webRequest.onErrorOccurred.addListener((details) { requestLog.push({ url: details.url, method: details.method, statusCode: 0, error: details.error, time: details.timeStamp }); }, { urls: [all_urls] });这里我故意用timeStamp来排顺序。details.timeStamp是事件发生时刻戳能精确反映请求发生顺序。之后在生成 HAR 时再把这个时间戳格式化成startedDateTime。5.4 打开禅道自动填表生成 HAR 之后数据会存在chrome.storage.local里然后通过chrome.tabs.create打开禅道新建 Bug 页面。填表逻辑复用另一段 content script从 storage 读数据按上一章的setNativeValue思路逐字段填充。这里要提醒的是不要在 popup 关闭后还在后台用storage.local存超大 HAR。如果 HAR 超过 5MB建议只存精简字段附件单独通过dataUrl或者临时文件的方式在真正提交时才组装。我们吃过亏存了个 12MB 的 HAR 到 storage结果扩展直接白屏。5.5 安全与合规提醒工具虽好但使用边界要明确。HAR 文件里包含了请求 Cookie、Token、Authorization 头等敏感信息生成后不要随意外发。扩展应限制在团队内部域名使用不要发布到公共扩展商店如果内部有安全审计要求还需要对 HAR 中的敏感字段做脱敏处理。我在实际工件里加了一个开关默认打开“脱敏模式”把 Authorization 头替换成***。6.1 请求列表为空点按钮生成的 HAR 没有任何条目这个问题第一次跑通时十有八九会碰到。排查顺序如下确认扩展权限里包含了webRequest并且host_permissions覆盖了被测页面域名打开扩展后台 Service Worker 的日志看监听回调里有没有数据进入requestLog注意安装扩展之前已经加载完成的页面历史请求是拿不到的刷新页面后再测试如果页面有 Service Worker 缓存或者 HTTP 缓存某些请求可能根本不经过网络层需要用 performance 补全确认performance.getEntriesByType(resource)里有数据。最后一个原因最隐蔽。webRequest在有些私有网络请求、WebSocket 升级请求上触发方式不一样所以永远要用 performance 数据做兜底。6.2 版本下拉框匹配不中版本匹配是重灾区。原因通常是禅道的版本下拉框选项加载慢或者是版本格式不一致。我的建议是先在 console 里把所有候选版本打出来看一眼然后对“识别到的版本号”和“候选版本”做格式化统一去掉v前缀、去掉空格、统一大小写再做includes判断。如果还是匹配不到就不要强选把版本号写进描述字段同时给“影响版本”选一个兜底值保证能提交。6.3 富文本描述塞不进去禅道的富文本编辑器在现代版本里是基于编辑器封装的直接用innerHTML赋值会被框架覆盖。可靠的做法是先定位到textarea或contenteditable元素用原生的 Document.execCommand 插入文本或者直接调用编辑器的公开 API如果有暴露全局对象的话。如果没有暴露就在有焦点时模拟粘贴事件把文本通过ClipboardEvent传进去。这个是最坑的一环我从“手动触发 input 事件”一直试到“模拟粘贴”才找到稳定方案。6.4 HAR 附件体积过大上传失败HAR 的体积问题几乎无法避免因为content.text会包含响应体全文。我的处理是三层降级默认不保存响应体内容只记录content.size如果遇到状态码异常保存响应体但截断到前 2KB再提供一个“完整保存”开关仅在必要的时候开启。同时生成 HAR 后用gzip压缩一下文本50% 的压缩率基本都能达到。禅道上传的是压缩后的.har.gz开发那边也能正常解压查看。7. 我的几点使用体会7.1 它如何改变了我的测试习惯工具用了一个月后我发现自己最大的变化不是“提单变快”而是开始习惯盯着请求链路去做判断。以前看到一个页面报错我的第一反应是“界面显示不对”现在第一反应是“哪个接口挂的、参数是什么、耗时是多少、是不是依赖的服务有问题”。有了自动生成的 HAR我对每个 Bug 都能拿出比“肉眼可见”更硬核的证据。团队里的开发反馈Bug 单被打回的现象少了很多因为“HAR 就在附件里自己拉下来看请求参数和返回码基本能定位问题”。7.2 后续还可以怎么玩这个 Skill 的框架并不局限于禅道。把禅道动作器换成其他缺陷管理系统就是一套通用的“网页抓包 缺陷自动化”底座。我接下来准备做两件事一是把生成 HAR 的过程接上 CI让自动化测试跑完直接产出 HAR 附件二是把“用例草稿”升级成“接口变更对比”统计同一个接口在两个版本之间的响应差异。到时候再写一篇单独的实战记录分享出来。最后分享一个我踩过的小坑扩展版本更新时如果用户还停留在旧页面上content script 不会自动重新注入一定要让用户刷新页面再用。这个看似不起眼的问题曾经让我们在演示时翻车不得不重启浏览器。工具写得好不好是一回事什么时候触发加载、什么时候重新注入这些细节决定了它在真实工作流里到底顺不顺手。