打开 Azure DevOps 的 pipeline 页面,点进去一个失败的 E2E 测试结果,看到的是几乎空白的日志。你不知道测的是哪个版本,不知道集群里发生了什么,不知道那个 409 Conflict 到底是谁返回的。你唯一的选择是去下载一个 Cypress 的回放视频,然后在一个模糊的浏览器录屏里试图找到那一帧报错。
这就是我们过去一整年跑 E2E 的体验。
这条路为什么走不通
先把系统拓扑摆出来:
GitHub (代码) -> Piper build -> GitOps bump -> ArgoCD sync -> ADO trigger E2E -> 结果藏在 ADO
代码在 GitHub 上,CI 在 Azure DevOps 上,部署通过 ArgoCD,E2E 又回到 ADO。开发者提交一个 commit,不会直接触发 E2E;E2E 的触发藏在 GitOps 仓库的文件变动后面,中间隔了 build、version resolve、gitops bump 三层间接。等你想看结果的时候,要离开 GitHub,去 ADO 的另一个 pipeline 里找。
这不是某个环节出了问题,而是整条链路的拓扑结构决定了反馈回路是断裂的。
四层原因,50-30-15-5
花了一些时间拆解,到底是什么在制造痛苦。结论是四层原因叠加,但权重非常不均匀:
CI 系统割裂,占 50%。 这是最大的问题,而且跟 Cypress 没有任何关系。你把任何测试框架放进同样的拓扑 – GitHub 写代码、ADO 跑测试、结果藏在 ADO – 开发者的体验都会一样烂。反馈回路断了,这不是测试框架能修复的事。
测试基础设施投入不足,占 30%。 Pipeline 里信息几乎为空,不是 Cypress 不能产出信息,而是没有人配置过 reporter。Cypress 能做 mochawesome 报告、失败截图、自定义 task hook,但这些都需要搭建。测试用例写了,基础设施没建,相当于买了车但没装仪表盘。
现场保全机制缺失,占 15%。 E2E 失败的时候,你想知道的不只是"哪个断言挂了",还有集群里发生了什么 – pod 的日志、Kubernetes events、当前部署的 image tag。这些信息在测试失败的那一刻是存在的,但没有人去抓。任何框架都不会自动帮你 kubectl logs,这是 pipeline 工程的活。
框架本身的局限,只占 5%。 Cypress 在 2026 年不是最优选择,但它也不是我们痛苦的主因。Playwright 的内置 trace、结构化 JSON 输出、多 reporter 原生支持确实更好,但如果只换框架不动其他三层,解决的只是 5% 的问题。
这个比例分配很重要,因为它决定了行动的优先级:先修拓扑,再补基础设施,框架迁移反而是顺手的事。
目标状态:三条命令拿到全部现场
理想中的 E2E 应该是什么样?
开发者把代码合到 develop,几分钟后 GitHub 上出现一个绿钩或红叉。如果是红叉,点进去就能看到哪个用例挂了、错在哪一步。如果需要深挖,下载 artifacts 就有完整的 Playwright trace、集群日志、pod 状态。
而对于 AI 来说,整个排查链路浓缩成三条命令:
gh run list --workflow e2e.yaml --branch develop --status failure
gh run download <run-id> -n e2e-report
cat e2e-report/failure-summary.json
第三条命令的输出告诉 AI 一切它需要知道的:测了什么版本、哪个用例挂了、错误消息是什么、去哪里找 trace 和集群日志。AI 不需要打开浏览器,不需要登录 ADO,不需要下载视频。结构化的 JSON + 纯文本日志,这是 AI 最擅长处理的格式。
架构设计
把所有环节串起来,完整的架构长这样:
PR / Push to develop
|
v
GitHub Actions workflow 触发
|
v
部署到测试环境(或等待 ArgoCD sync 完成)
|
v
Health check 轮询直到服务就绪
|
v
Playwright 执行测试
|
+-- Pass --------+
| |
+-- Fail |
| | |
| v |
| Context |
| Collection |
| - kubectl logs|
| - k8s events |
| - pod describe|
| - image tags |
| | |
| v |
+--------+-------+
|
v
Upload Artifacts
- HTML report (人看)
- JSON results (AI 读)
- Traces .zip (深度调试)
- Screenshots (多模态 AI)
- Cluster context (现场)
- failure-summary.json (AI 入口)
|
v
GitHub Check 显示 pass/fail
PR 上直接标注失败位置
几个关键的设计决策:
Playwright 而不是继续用 Cypress。 不是因为 Cypress 不能用,而是 Playwright 把大量基础设施内置了。HTML 报告内置、JSON reporter 内置、trace 内置、失败截图默认开启、GitHub reporter 能直接在 PR diff 上标注失败位置。一个 playwright.config.ts 搞定,不需要装 mochawesome、不需要写 plugin:
export default defineConfig({
reporter: [
['html', { open: 'never' }],
['json', { outputFile: 'results.json' }],
['github'],
],
use: {
trace: 'retain-on-failure',
screenshot: 'only-on-failure',
video: 'retain-on-failure',
},
});
GHA Artifacts 而不是外部对象存储。 10GB 容量、90 天保留期、gh run download 一键拉取。对于测试报告这个场景完全够用,而且 AI 通过 gh CLI 就能直接访问,不需要额外的认证链路。只有当你需要跨 repo 共享或超过 90 天历史趋势分析时,才值得引入 S3 之类的外部存储。
failure-summary.json 作为 AI 的唯一入口。 这是整个设计里最关键的一个文件。测试结束后自动生成,把 Playwright 的 JSON 结果和集群上下文信息合并成一个自包含的摘要。AI 读这一个文件就知道该去哪里深挖。不用猜目录结构,不用遍历文件,一个 JSON 告诉你全部的路径。
为什么最终消费者是 AI
这个判断不是赶时髦。
E2E 测试失败后的排查,本质上是一个信息检索和模式匹配的任务:从大量的日志、事件、状态信息中,找到那个关键的因果链。人类做这件事需要在多个窗口之间切换、用肉眼扫描日志、在脑子里关联时间线。而 AI 天然擅长这些:给它结构化的数据,它可以在几秒内关联 Playwright 的错误消息、对应时间点的 pod 日志、Kubernetes events,然后给出一个合理的根因假设。
但前提是信息要以正确的格式喂给它。视频是最差的格式 – AI 很难从模糊的浏览器录屏中提取有用信息。ADO pipeline 的页面需要浏览器交互才能访问,AI 也很难自动化。而 JSON + 纯文本日志 + PNG 截图,这是 AI 处理效率最高的组合。
所以这个基础设施的设计哲学不是"让人更容易 debug",而是"让 AI 能拿到它需要的一切,然后让 AI 来 debug"。人类是最终决策者,但初步排查完全可以自动化。
实施路径
拆成可执行的步骤:
- Playwright 项目初始化 –
npm init playwright,配好playwright.config.ts,把 reporter、trace、截图策略一步到位。 - GHA workflow –
e2e.yaml,定义触发条件(push to develop)、部署等待、测试执行、artifact 上传。 - Context collection 脚本 – 测试失败时自动执行
kubectl logs、kubectl get events、kubectl describe pods,输出到文件。 - failure-summary.json 生成 – 读 Playwright JSON 输出 + 集群信息,合并成 AI 入口文件。
- 测试用例迁移 – 从 Cypress 语法转换到 Playwright。这是体力活,可以渐进式做,先迁移核心路径。
其中 1-4 是基础设施,做一次受益永久。5 是增量工作,可以边跑 Cypress 边迁移,不需要一刀切。
这件事的意义
回过头来看,过去一年的痛苦不是因为 E2E 测试本身有问题,而是因为 E2E 的基础设施和开发者的工作流脱节了。测试跑了,但结果藏在另一个系统里;测试挂了,但现场信息没有保留;想排查,但可用的数据几乎为零。
把 E2E 搬回 GitHub Actions、用 Playwright 替代 Cypress、加上 context collection 和 AI-first 的报告格式 – 这不是在修补一个旧系统,而是在重建整个反馈回路。当这套基础设施就位之后,E2E 测试才真正变成开发流程的一部分,而不是一个跑在某个角落里、偶尔告诉你坏消息、但从不告诉你为什么的黑箱。