别让模型每次 CI 都重跑:混合式 e2e 怎么拆步骤

团队里谈「用 AI 做端到端」时,常见两种落地,最后都不太好维护。

一种是让模型生成 Playwright 脚本,生成完就冻结成定位器。当时省事,按钮文案或层级一改,红的还是选择器,修的还是人。模型只出现在写脚本的那一下。

另一种是整条用例都写成自然语言,每次运行都让模型看页面、推理、点击。路径灵活,但没改过的页面也会重复烧 token,而且模型当场决策,CI 里今天绿、明天红并不稀奇。

TesterArmy 的 e2e 走的是第三条:同一条测试里,不确定的路径交给 agent,必须稳定的结果交给定位器和断言。仓库 README 把这件事概括成:用自然语言描述目标,由 agent 驱动应用达成;结果仍用 locator 和断言检查。协议是 Apache-2.0,覆盖 Web 和移动端。

下面不谈热度,只谈已经有自动化套件的团队该怎么拆。

从左到右是确定性断言、动作回放、agent 看页面,只是概念示意,不是产品截图或实测数据。

一条测试里谁负责什么

可以按失败成本来分,而不是按「是不是 AI 项目」来分。

  • 结果必须可重复核对:留给 expect。金额、状态文案、是否进入某个路由,这些不应该交给模型「看起来对了」。
  • 路径长、选择器脆、改版频繁:交给 agent.act()。你描述目标,不描述第几个 div。
  • 这条用例根本没有 agent 步骤:就不该产生模型调用。README 写明,没有 agent 步骤的测试不需要模型。这意味着可以一条一条往现有套件里加,而不是新起一套纯 AI 回归。

自拟一个邀请加入团队的场景,只用文档里已经公开的写法(app.open、agent.act、agent.assert、screen.getByRole):

ts 复制代码
import { test, expect } from 'e2e';

test('invitee joins the default team', async ({ app, agent, screen }) => {
  await app.open('/invite/accept');

  await agent.act('accept the invite and stay on the default team');
  await agent.assert('the header shows the invited team name');

  await expect(screen.getByRole('status')).toContainText('joined');
});

agent 负责把邀请流程走完;最后一行仍然是普通断言。审用例时看的是「目标是否说清楚、断言是否钉死结果」,不是逐行看选择器。

回放改变的是账单,不是正确性

混合本身解决不了费用。贵的是「每次 CI 都重新推理」。

e2e 的做法是:被后续断言验证过的 agent 步骤会记下动作;下次应用没变,就回放这些动作,不再调模型。应用变了、回放对不上,才重新规划,并记下新动作。README 的原话是:next run replays them with no model calls until the app changes。

对排期有用的推论是:模型费用应该跟着「这周真正改过的界面」走,而不是跟着回归条数走。十个模块里只有结算页改了,就只该为结算相关的 agent 步骤付钱,其余回放。反过来,如果你们的页面每周都大改,回放几乎总是失效,那这层节省就不成立,不该为了「AI 测试」硬迁。

正确性仍然靠断言,不靠回放。回放只是在界面没变时跳过推理;断言失败就说明结果不对,该修的是产品或用例,不是缓存本身。

和现有技术栈怎么接

Web 引擎是 @e2e-dev/web,浏览器走 Playwright,Chromium、Firefox、WebKit 都在包说明里。移动端是 @e2e-dev/mobile,打到 iOS 模拟器和 Android 模拟器。测试写法仍是同一套 test / expect / agent 步骤,差别主要在引擎和夹具,而不是再学一套断言库。

官方 examples/ 里已经有可以单独跑通的工程:Vite、Next.js、Astro、Angular、Expo、SwiftUI、Jetpack Compose、Kotlin Multiplatform、Flutter、.NET MAUI。选型时先对一下自己的壳,比先看 star 有用。

从 Playwright、Cypress 这类框架迁过来,值得迁的通常不是全部定位器,而是那些已经反复修、又很难写稳的步骤。稳定的 getByRole 不必换成自然语言。

安装入口就是:

bash 复制代码
npx e2e init
npx e2e run

init 会问引擎(Web 或 Mobile)和模型来源,然后写下配置和一条示例测试。建议先把「不调模型的示例」跑绿,再加 agent 步骤。Node 版本、Windows 是否要 WSL、模拟器要不要本地 Xcode / Android SDK,以 quickstart 当前说明为准,不要凭旧文里的版本号。

模型、密钥和给编码代理看的文档

模型不是框架内置的。README 写的是自带订阅、API key,或本地模型。配置集中在 init 生成的 e2e.config.ts。密钥放环境变量,不要写进用例或系统提示词;报告里也不该出现明文。

另一件和「人写脚本」不同的设计是:整套文档打进 e2e 这个 npm 包,路径在 node_modules/e2e/docs。编码代理可以离线读,不必临时去爬官网。首页还给代理准备了 npx skills add tester-army/e2e。实际分工可以是:人规定哪些结果必须断言,代理起草 agent 步骤,人看失败报告里的步骤、截图和 trace。

包也不只本地这几个。除了主包 e2e、Web、Mobile,还有把结果贴到 GitHub PR 的 @e2e-dev/github,以及 Kernel、Lightpanda、smol、EAS、Limrun、TestMu 这类托管浏览器或模拟器。本地还没稳定时,不必先接云端包。

CLI 会上报匿名使用数据(跑了哪条命令、哪个引擎、失败落在哪),不包含用例内容、应用内容和凭证。不想要就 npx e2e telemetry disable,或设置 E2E_TELEMETRY_DISABLED=1。

现在不该整库替换的原因

仓库自己标了:走向 1.0 的过程中,minor 之间 API 和配置仍可能变。核心回归如果今天就全押上去,要接受跟着改配置。

第一次执行 agent 步骤才会真正调模型,界面越复杂越贵。回放能压住之后的 CI,压不住「第一次全量铺开」那次账单。可以先挑三五条最脆的路径做试点,看回放命中率和失败是否可诊断,再决定铺开范围。

还有一层边界:开源框架是 Apache-2.0;TesterArmy 另外有一个在 PR 或定时任务上跑自然语言测试的托管平台。用框架不等于买平台,后续是否收费以他们的产品说明为准,不要把这两件事写成同一个交付物。

可以带走的判断

工具可以先不装,拆分方式可以先用在现有套件上。

  1. 断言守住必须稳定的结果,agent 只吃写不稳的路径。
  2. 模型调用应当发生在界面变化之后,而不是每一次 CI。
  3. 没有 agent 步骤的老用例继续当普通测试跑,迁移按条进行。

仓库:github.com/tester-army...

文档:e2e.tester.army/docs

本文按仓库 README 和文档里的公开能力重新整理,示例是为了说明 API,不是复述某篇评测的数据或结构。star、下载量和第三方背书会变,选型时以你自己的套件能不能回放、断言能不能钉死结果为准。

相关推荐
用户766918404481 小时前
抓包两小时,脚本写一天:接口自动化的“最后一公里”,到底卡在哪?
测试
lyai0071 小时前
garak 入门:别急着扫,先看懂一条数据是怎么被判“命中”的
aigc·agent
王中阳Go1 小时前
面试官问什么,取决于你简历上写了什么:后端转 Agent 岗的项目准备法
后端·面试·agent
Topskys1 小时前
Deep Research 深度研究模型
agent
张一凡932 小时前
AI 改完代码,人却不知道它改了什么:架构漂移才是真正的成本
前端·后端·agent
JJJennie7773 小时前
MAI Gateway私有化部署全攻略:大模型网关怎么部署?附架构图
开源·gateway·ai网关
武子康3 小时前
LingBot-VLA 2.0 怎么从 6B 权重走到真实机器人?55 维动作只是起点
人工智能·后端·agent
千桐科技3 小时前
qData 数据中台开源版 v1.6.4 发布:聚焦使用反馈,功能细节与产品体验持续优化
数据库·开源·qdata
网络豆4 小时前
开源鸿蒙平台 CMP 三方库「kmp-compose-tab-container」适配全流程
华为·开源·harmonyos