事情是这样的。
最近有个事儿让我挺感慨。一个做电商的朋友跟我吐槽,说他们团队上了AI编程工具之后,代码产出速度翻了三倍,但上线事故也翻了一倍。
我说你这不对啊,代码写得快不是好事吗?
他说问题是,代码写得快了,测试跟不上了。
这句话一下子戳到了我的痛点。
你想想看,现在用Cursor、Claude Code写代码,一个下午能撸完一个功能模块。但你要给这个模块写端到端测试呢?还是得一个一个选择器去定位,一个一个断言去写。UI稍微改一下,测试全红。
AI帮我们把油门踩到底了,但刹车系统还是十年前的。
一、端到端测试的老毛病,所有人都在忍
坦率的讲,做过E2E测试的人都知道那个循环有多折磨人。
写测试的时候,你要告诉程序「点这个按钮」「填这个输入框」「检查这个文本」。听起来很简单对吧?但问题是,这些操作全靠选择器来定位。什么css选择器、xpath、data-testid,你得精确地告诉程序「去点哪个元素」。
然后呢?产品说我们要改版。
设计师把按钮换了位置,前端把class名重写了,或者干脆把整个页面结构重构了一遍。你的测试选择器全部失效,红成一片。
我有时候觉得,写E2E测试最痛苦的不是「写」,而是「改」。改选择器这个活,枯燥、无聊、毫无技术含量,但你又不得不做。它就像你家水龙头漏水,你明知道该修,但每次都是先拿盆接水,等实在忍不了了才动手。
很多人干脆就不写了。或者说,写个核心流程意思意思,剩下的全靠手动回归。
这就是现状。
二、纯AI驱动?听起来美好,但有个致命问题
有人说,那让AI来点不就行了?你告诉AI「帮我升级到Pro套餐」,它自己去找按钮,自己去点。
确实,现在有不少工具在做这件事。用大模型驱动浏览器,自然语言描述目标,agent自己规划路径去完成。
听起来完美。
但你要是把这个方案放到CI里跑,三个问题立刻浮出水面。
每次都得花钱调模型。 同一条流程,昨天跑过,今天还得再跑一遍。你的测试每天跑十次,每次都要调API,token费用蹭蹭往上涨。
结果不确定。 模型这次点了「升级」按钮,下次可能点了旁边的「了解更多」。回归测试最怕这种漂移,你连「这条测试到底是通过了还是碰巧通过了」都分不清。
判断不硬。 如果「成功了吗」也交给模型来判断,等于让被判者自己批改卷子。模型说「我觉得成功了」,你敢信吗?
所以对要上CI的团队来说,纯LLM驱动的测试,通常就是个demo级别的东西。演示的时候惊艳四座,真正放到日常流程里跑,成本和稳定性都撑不住。
这就是那个矛盾所在,自然语言的灵活性很诱人,但可重复、可审计的确定性不能丢。
怎么把这俩东西缝到一起?
三、e2e的解法,让AI做它擅长的,让程序做它擅长的
最近在GitHub上看到一个开源项目,叫e2e,TesterArmy团队做的。Apache-2.0协议,100% TypeScript。看了它的设计思路之后,我觉得这帮人想明白了一件事。
不是所有步骤都需要AI,也不是所有步骤都适合写死选择器。
你在一个测试里,有些操作是确定性的。比如「检查页面上显示Pro」,这个用一个精确定位器加上断言就行了,不需要AI。
但有些操作是模糊的。比如「升级到Pro套餐」,这个过程可能要点好几个按钮、填表单、选方案,而且UI经常改。这种操作用自然语言描述目标,让agent自己去完成,反而更合适。
e2e的做法是把这两种方式放在同一个测试里。你可以用agent.act('升级到Pro套餐')来描述目标,用expect(screen.getByRole('status')).toContainText('Pro')来精确检查结果。
test('a member upgrades to Pro', async ({ app, agent, screen }) => { await app.open('/settings/billing'); await agent.act('upgrade the workspace to the Pro plan'); await expect(screen.getByRole('status')).toContainText('Pro'); await agent.assert('the invoice preview shows the Pro price');});
输入是自然语言,动作是agent自己规划的,结果是硬断言钉死的。
这个设计我觉得挺聪明的。它没有试图用AI解决一切,也没有固守传统选择器不放。而是让两者各司其职。
四、真正让我觉得「卧槽」的,是它的缓存机制
如果只是把agent和assertion放在一起,那也就是个语法糖。真正让我觉得这帮人想透了的,是它的缓存设计。
一次通过,永久回放。
当一个agent.act步骤通过了,并且后续的检查也确认了结果,e2e会把这个agent走过的路径录制下来,存进缓存。下次再跑这条测试的时候,它直接重放这些动作,不调用任何模型。
不花一分钱,不等一秒钟。
但关键来了,这个回放不是机械重演。
录制的价值不在于「记下来」,而在于「被验证过」。
每条录制都带一组锚点,这一步让哪些节点出现了,哪些消失了,留下的控件处于什么状态。回放的时候,只有当「该出现的出现了,该消失的消失了,回放期间确实发生了变化」,才算通过。
如果应用改了,终点状态对不上呢?它不会硬猜,而是直接把控制权交还给实时agent,让它重新找路径。
失败即降级,绝不错答。
这个思路我喜欢。它宁可多花一次模型调用,也不悄悄点错。在测试领域,误报比漏报更可怕。一条测试告诉你「通过了」但实际上没测到该测的东西,这种假安全感比没有测试还糟糕。
而且缓存键设计得极其细致,项目、测试、目标、平台、引擎版本、策略版本、指令摘要、参数摘要、调用序号、agent名称,全都是键的一部分。一条过期的录制只可能是「未命中」,不可能变成「错答」。
你想想这结果会怎样呢?
你的token花费,跟UI变化频率挂钩,而不是跟测试运行频率挂钩。
UI不变,测试跑一百次都是零成本。UI变了,只有受影响的步骤才会重新调用模型。
这才是AI测试该有的样子。
五、不只是Web,一套代码跑三端
e2e还有一个我觉得很值得关注的点,它不只是Web测试框架。
浏览器通过Playwright驱动,iOS模拟器和Android模拟器通过agent-device驱动。同一套测试代码、同一套定位器、同一套断言,三个平台通用。
export default {targets: [ { name: "web", engine: web(), app: { url: "http://127.0.0.1:3000" }, }, { name: "ios", engine: mobile({ platform: "ios" }), app: { bundleId: "com.example.app" }, }, ],agents: { default: { model: gateway("openai/gpt-6-luna-fast") }, },} satisfies E2EConfig;
对于同时有Web和移动端产品的团队来说,你不需要维护三套测试了。一套搞定。
而且它不绑定任何特定的AI服务商。OpenAI、OpenRouter、本地模型、ChatGPT订阅、GitHub Copilot订阅、SuperGrok订阅,你已有的东西都能用。它在token价格上不加一分钱。
这很重要。因为很多AI测试工具的商业模式就是卖你模型调用,价格还不便宜。e2e走的是纯开源路线,模型你自己带,它只负责框架。
六、给编码AI也开了一扇门
e2e还做了一件我觉得非常有远见的事,它提供了MCP server。
Claude Code、Cursor、Codex这些编码AI可以直接连接到你的应用,看到真实的UI,然后帮你写测试。
你不需要手动去抄选择器了。编码AI自己去应用里探索,找到真实的控件名称,然后生成带有精确locator的测试代码。
用他们的话说,写功能的那个AI,同时也是写测试的那个AI。
这个闭环如果跑通了,开发流程就变成了,AI写代码 → AI写测试 → CI自动回归 → AI修bug。人只需要做审核和决策。
当然,现在还没到那个程度。但方向是对的。
七、说说我觉得不够好的地方
坦率的讲,没有完美的工具。e2e也有它的局限。
首先,它是pre-1.0状态。API还可能变。如果你现在就把它放到核心业务的CI里,要做好后续跟着改的心理准备。
其次,agent步骤的成本问题。虽然有缓存,但首次运行和UI变化后的重跑,模型调用是实打实的花钱。对于测试用例特别多的大型项目,这个成本需要评估。
第三,纯agent步骤的可审计性还是不如传统选择器。虽然它设计了锚点验证机制,但在某些合规要求严格的行业,「AI自己找路径」这个事情本身可能就是个审计障碍。
最后,目前只支持Web和移动端。桌面应用还不支持。
但这些是迭代的问题,不是方向的问题。
八、回到那个根本问题
你有没有想过,为什么测试一直是开发流程里最容易被牺牲的环节?
赶进度的时候,第一个砍的就是测试时间。上线前发现没测全,临时加人手动回归。出了事故之后,老板问「为什么没测到」,开发说「用例没覆盖」,QA说「需求没说清楚」。
这不是任何一个人的问题,是整个范式的问题。
传统E2E测试的成本结构就是这样的,写的时候费劲,维护的时候更费劲,跑的时候还可能因为环境问题飘红。三重打击之下,大家自然不愿意写。
e2e做的事情,说到底是在改变这个成本结构。
它把「写测试」的成本降下来了,用自然语言描述目标比手写选择器省事得多。它把「维护测试」的成本降下来了,agent能自动适应UI变化。它把「跑测试」的成本降下来了,通过缓存机制把重复运行变成零开销。
当这三个成本都降下来的时候,写E2E测试就不再是一件需要「专门腾出时间」的事情了。它变成了开发流程里自然而然的一环。
就像你不会觉得「写代码之前打开编辑器」是一件需要额外花时间的事。它就是工作的一部分。
我自己的感受
说实话,我看到这个项目的时候是有点兴奋的。
不是因为它用了AI这个时髦的词,而是因为它没有被AI冲昏头脑。
它没有说「AI可以做一切」,它说的是「AI做AI擅长的,确定性做确定性擅长的」。这种清醒,在当下的AI工具浪潮里其实挺稀缺的。
我们见过太多「AI驱动」的工具,演示的时候惊艳,真正用起来要么太贵、要么不稳定、要么结果不可审计。它们的问题不是技术不行,是没想清楚AI在系统里到底该扮演什么角色。
e2e的答案是,AI是一个有弹性的执行者,而不是一个黑箱决策者。你告诉它目标,它去执行。但结果好不好,得由确定性的检查来判断。执行路径可以变,但验收标准不能模糊。
这个思路我觉得不只是适用于测试。很多AI工具的设计都可以借鉴这个哲学。
当然,我说的这些都只是我看了代码和文档之后的判断。我自己还没在生产环境里跑过e2e。可能有些想法还不成熟,等我实际用一段时间之后再来更新。
如果你的团队正在被E2E测试折磨,或者你正在评估AI测试方案,可以去看看这个项目。GitHub上搜tester-army/e2e,npx e2e init就能跑起来。
试试看,也许会有不一样的体验。
以上,既然看到这里了,如果觉得不错,随手点个赞、在看、转发三连吧,如果想第一时间收到推送,也可以给我个星标⭐~
谢谢你看我的文章,我们,下次再见。
/ 作者:mojianpo
/ 投稿或爆料,请联系邮箱:406223802@qq.com