让每个 PR 自动完成测试与修复闭环
作者为 AgentPulse 接入 PR 预览部署与 TestSprite 自动测试,先生成 33 个端到端用例,再补充 4 个输入校验用例。文章围绕一次合并前发现三个真实缺陷的实践,展示编码 Agent 的测试与修复循环。
AI 深度解读
AI 写完代码,单元测试全绿,PR 就能合并了吗?作者在 TestSprite Hackathon Season 4 做了一个 AgentPulse 仪表盘,把预览部署、端到端测试、Claude Code 修复和分支保护串成闭环。一次原本只改文档、补测试的 PR,最终暴露了三类真实问题。
这篇案例有价值的地方,是展示了检查如何从「失败了提醒一下」变成「失败就不能合并」,也留下了 React 表单、预览地址和测试触发时机上的几个具体教训。
先让测试跑到当前 PR,再让失败挡住合并
AgentPulse 面向使用 AI 编程代理的团队,包含运行可靠性监控、流水线 DAG 编辑器、测试场景仓库、混沌与 Webhook 模拟,以及查看「写代码 → 测试 → 修复 → 再测试」过程的检查器。
作者先用 TestSprite 3.0 网页控制台中的 agentpulse-pi 项目抓取已部署站点,生成了 33 个端到端测试,覆盖 Fleet Overview、Pipeline Studio、Test Scenarios、Self-Healing Verification Loop、Chaos Testing & Webhook Simulation 五类用例。之后又补了四个输入校验用例,总数变成 37。
真正关键的是后面的接线:
- Vercel 连接 GitHub,每个 PR 都生成独立预览部署。
- 在控制台的 GitHub Action 页签配置触发器,监听 Preview deployment 事件,目标为
https://agentpulse-git-{branch-slug}-<team>.vercel.app。 - 在
main的分支保护中,把TestSprite V3设为必需检查,管理员也不能跳过。
测试必须访问当前 PR 的代码,失败也必须真正阻止合并。
作者还纠正了一个容易造成误判的命名:仓库原有任务叫 TestSprite 3.0 Verification Gate,实际只执行 npm test,再运行一个没有凭据的归档 Action。后来它被改名为 Build & Unit Tests,真正的 TestSprite App 检查使用自己的名称。检查名字听起来可靠,不代表它真的覆盖了用户操作。
从 33/37 到 37/37,问题藏在哪几层
第一次运行只有 33/37 通过,合并被阻止。表单允许预期 HTTP 状态码填入 0、999 或留空;空值还会出现存储时使用 200、断言却保留原始值的不一致。目标路径带首尾空格时,也会因为先检查开头的斜杠、后去空格而被拒绝。
Claude Code 的第一轮修复,是把校验提取到不依赖框架的小模块并补上单元测试:状态码必须是 100 到 599 的整数,标题和路径先去除首尾空格。自动重跑后,结果升到 35/37。
空值问题仍然存在,原因已经超出了纯校验函数的范围。测试用脚本清空输入框时,DOM 发生了变化,却没有触发 React 的 onChange。提交处理函数仍然读到 state 中旧的 "200",于是放行。
作者认为,真实用户遇到的自动填充也可能造成类似情况。这里可以确定的是这次脚本操作复现了 DOM 与 React state 不一致;原文没有提供不同浏览器自动填充行为的验证。
第二轮修复改为提交时通过 FormData 读取表单实际值,结果达到 36/37。要盯住的是提交时从哪里取值:校验规则写对了,输入来源错了,结果仍然会错。
最后一个失败来自导航。Fleet Overview 虽然列出了被监控的流水线,却不能打开指定流水线,View Pipelines 总是进入第一条。作者给每张卡片加上 View Details,在 Pipeline Studio 打开对应项目,最终达到 37/37,合并门禁放行。
原文没有解释,这个导航失败为什么没有出现在最初对四个失败的描述中。能确认的是,它在后续修复过程中被发现并解决,不宜把每轮失败简单理解为固定不变的一组用例。
预览环境的三个小坑,会让整条闭环失效
第一是访问权限。作者在自己的 Vercel Hobby 配置中遇到预览部署默认需要登录,TestSprite 因而可能测到登录页。他的应用是无登录的公开演示,所以关闭了预览保护。这个处理有明确前提,不能直接照搬到内部系统。
作者还提醒,不要把绕过保护的 secret 放进目标 URL,因为 TestSprite 会在公开 PR 评论中打印该地址。
第二是域名长度。Vercel 会截断过长的预览子域名,63 字符的主机名限制会让按 {branch-slug} 拼接的地址失效。作者使用短分支名 verify-gate,让地址保持可预测。
第三是事件时机。触发器只接收创建之后的新事件。作者配置触发器时,PR 的首次预览已经完成,因此需要重新部署一次,才能启动首轮测试。
这些问题都发生在测试断言之外,却决定了测试到底有没有跑到正确页面。
我的判断
我认同这个案例对前端团队的提醒:纯函数单测覆盖不到所有浏览器行为。DOM、React state、事件触发和页面导航之间的衔接,需要在真实页面里验证。作者的单测始终通过,而端到端测试抓住了状态不同步,证据很具体。
但它仍是一次 Hackathon 项目的经验记录。作者没有给出 TestSprite 与手写 Playwright 测试的对照,也没提供误报率、执行耗时、费用或长期稳定性数据。37/37 只能说明这套用例通过,无法证明整个应用没有问题。
失败报告带截图、观察结果和修复提示,确实方便交给 Claude Code 继续处理。不过,现有端到端测试同样可以接入预览部署和分支保护。这篇验证了闭环有用,尚不足以证明某个工具是唯一选择。
能拿来做什么
- 给前端 PR 配置端到端测试时,先核实它访问的是该 PR 的预览地址,再把相应检查加入分支保护,让失败结果进入合并决策。
- 排查 React 表单时,同时检查校验规则与取值来源;遇到脚本填值、自动填充等路径,可以针对 DOM 与 state 不一致补充浏览器测试,并评估提交时使用
FormData。 - 把输入范围、空值、首尾空格和卡片到详情页的对应关系写成明确用例,这些都是作者实际发现问题的入口。
- 向 AI 编程代理提交修复任务时,附上失败步骤、实际结果、预期结果和截图;提交修复后重跑相关测试,用结果确认问题是否解决。