上周我被测试群里一张截图弄得有点沮丧。一条跑了三年的核心下单链路用例,因为前端把登录按钮的id从login-btn改成submit-login,一夜间红了 26 条。新同事说改选择器就能解决,但没有人知道这个按钮在第三个版本里还会不会再改一次。这种时刻我意识到,传统 UI 自动化最贵的成本不是写用例,而是维护用例。于是我开始认真看 midscene 这个方向:一个用自然语言驱动浏览器、让 AI 智能体自己"看页面、做操作、验结果"的框架。
这篇文章我会把 midscene 智能体做 AI 自动化测试的环境搭建、Demo 演示完整展开,也会把"midscene 被 Playwright 调用的原理"这一部分讲透。我不打算只给命令,还会解释每一步为什么这么做、实测中会遇到哪些坑、怎么排错。适合正在评估 AI Agent 做 Web 自动化测试的团队,也适合想亲手跑通第一个 AI 测试 Demo 的个人开发者。不管你之前用的是 Selenium、Playwright 还是 Appium,这篇文章的思路都能接上。
1. 传统UI自动化的脆弱性与midscene智能体的解题思路
1.1 一次前端改版引发的用例雪崩
先还原一下开头那个场景。老用例长这样:
// 用了三年,突然红了 await page.click('#login-btn');前端把按钮id改成>mkdir midscene-demo cd midscene-demo npm init -y npm i -D @midscene/web playwright typescript tsx npx playwright install chromium
每一步我都解释一下为什么这么做:
npm init -y:生成一个默认的package.json,给项目一个包管理入口。npm i -D @midscene/web playwright typescript tsx:一次装齐四个依赖。@midscene/web是智能体 SDK;playwright是浏览器自动化框架;typescript用来写 TS 脚本;tsx是一个可以直接运行 TypeScript 文件的工具,省去单独的编译步骤。npx playwright install chromium:下载 Chromium 浏览器内核。这一步特别容易被人跳过,因为"刚才明明按了 playwright,怎么跑不起来"?原因很简单:playwright 的 npm 包只是客户端,浏览器内核需要单独下载。如果下载超时,先检查本机网络能否正常访问外部网络和 DNS 解析,不要跳过这步,后面所有脚本都依赖这个浏览器内核。
3.3 模型参数的环境变量配置
先把模型的三个环境变量配好:
export AI_API_KEY="你的模型服务密钥" export AI_BASE_URL="你的模型服务商兼容接口地址" export AI_MODEL="gpt-4o 或 qwen-vl 等同类型支持视觉的模型ID"这里有一个很关键的实践:绝对不要把密钥写死在代码里。一方面,测试脚本经常会提交到仓库里,密钥一旦提交就等于泄露;另一方面,CI 环境里换模型只需要改环境变量,不需要动代码。团队内部如果统一走模型网关,也可以把AI_BASE_URL指向网关地址,方便统一计费和审计。
配完之后,可以用下面的命令验证一下环境变量是否已经生效:
echo $AI_API_KEY如果输出为空,说明当前终端会话没有拿到变量,检查一下是否用了错误的变量名。
3.4 用最小脚本验证"智能体真的能操作页面"
环境装好之后,写一个最小的验证脚本,业务逻辑越简单越好,避免把问题混在一起。
新建first-run.ts:
import { chromium } from 'playwright'; import { PlaywrightAgent } from '@midscene/web'; async function main() { const browser = await chromium.launch({ headless: false }); const page = await browser.newPage(); const agent = new PlaywrightAgent(page); await page.goto('https://www.saucedemo.com/'); await agent.runTask('在用户名输入框填入 standard_user,在密码输入框填入 secret_sauce'); await agent.runTask('点击 LOGIN 按钮,并断言页面出现 Products 标题'); await browser.close(); } main();运行:
npx tsx first-run.ts如果你装到的 midscene 版本入口路径不同,比如@midscene/web/playwright下导出,以你装到的版本提示为准,核心用法不变:创建PlaywrightAgent,传入 Playwright 的page对象,然后runTask下指令。
跑起来之后,你会看到浏览器窗口自己打开,自动填入账号密码,点击登录,然后进入商品列表页。这一步跑通,说明你的环境基本没问题。
4. 可复现的Demo:自然语言驱动登录与自动验证
4.1 目标场景选型:为什么选一个公开练习站
选场景是有讲究的。第一次跑 AI 智能体,千万不要拿公司内部复杂的后台系统练手,因为任何一个环境变量、权限、弹窗问题,都会让你分不清是环境问题还是智能体问题。
我推荐用 SauceDemo 这个公开练习站。它专门为自动化测试设计:登录账号固定、商品列表稳定、购物车流程完整,页面结构也不复杂,非常适合验证"AI 智能体到底能不能看懂页面并执行流程"。
场景设计如下:
- 打开 SauceDemo 首页。
- 用 standard_user 登录。
- 进入商品详情页,把 "Sauce Labs Backpack" 加入购物车。
- 进入购物车页面,断言商品存在,并取回价格。
这个流程覆盖了输入、点击、跳转、断言、数据提取几个核心能力,足够说明问题。
4.2 完整Demo脚本:登录、断言、再到加购
新建demo-flow.ts:
import { chromium } from 'playwright'; import { PlaywrightAgent } from '@midscene/web'; async function main() { const browser = await chromium.launch({ headless: false }); const page = await browser.newPage(); const agent = new PlaywrightAgent(page); await page.goto('https://www.saucedemo.com/'); await agent.runTask('完成登录:使用 standard_user 登录,密码 secret_sauce,然后点击登录按钮'); await agent.runTask('在商品列表中点击名称为 "Sauce Labs Backpack" 的商品图片进入详情页'); await agent.runTask('点击 Add to cart 按钮,然后点击购物车图标'); await agent.runTask('断言购物车页面里商品名包含 Sauce Labs Backpack,并输出商品价格'); await browser.close(); } main();这里四个runTask分别对应四段意图。你会发现,整个脚本里没有任何一个 XPath 或 CSS 选择器。所有定位工作全部交给智能体完成。
4.3 运行链路解读:规划日志、执行日志与截图痕迹
跑这个脚本的时候,控制台会输出类似下面的信息:模型当前认为页面处于什么状态、计划执行哪些动作、动作用了什么参数、执行结果如何。建议你在跑的时候打开两个终端,一个跑脚本,一个观察运行目录。
我实测中的输出节奏大致是:
- 模型收到任务"完成登录",先观察页面截图。
- 模型产生规划:第一步定位用户名输入框,第二步定位密码框,第三步定位登录按钮。
- Playwright 依次执行点击、填入、再点击。
- 模型再次截图,确认页面已经跳转到商品列表页,任务完成。
注意,这个 Demo 跑通后,项目目录里往往会生成报告或截图文件。这些痕迹文件是调试的关键:如果某一步出了问题,你可以打开当时的截图,看模型眼中的页面和你眼中的页面是否一致。很多时候你以为页面长这样,其实弹窗、蒙层、加载态把关键元素挡住了。
4.4 断言失败时智能体的自主容错行为
传统自动化里,断言失败就是用例失败,红色一片。midscene 的机制不太一样:runTask里的"断言"更像一个目标校验步骤。如果第一次断言失败,智能体不会直接放弃,它会重新截图、重新思考,尝试修正操作。
举个实际例子:如果登录按钮被一个弹窗遮住了,传统脚本会直接超时报错。midscene 智能体则会先"看到"有个弹窗,然后判断这个弹窗是否需要关闭,再执行关闭动作,最后继续登录。
我把这个特性叫做"智能体自愈"。它非常像真人测试员的工作方式:第一次没找到按钮,先看看是不是被什么挡住了,处理完障碍再试一次。这也是它解决"UI 自动化脆弱性"的核心价值——不是消除错误,而是在出错时有能力自己纠偏。
当然,自愈不是万能的。如果一个元素压根不存在,或者页面直接白屏,模型重试几次仍然会失败。所以后面要讲重试策略和失败现场保护。
5. 接进现有测试体系:与pytest、Appium、接口自动化共存
5.1 不推翻pytest:让智能体管"看得见的流程"
我见过不少团队聊 AI 自动化测试,第一反应是"是不是要把现有的 pytest 框架全换掉"?完全没必要,也不应该。
pytest 这类框架最擅长的东西:测试编排、数据断言、fixture 管理、报告输出、CI 集成。这些能力 midscene 替代不了,也不应该替代。midscene 最擅长的东西是:看得见的 UI 流程。所以合理的架构是分层协作:
- pytest 负责调度和断言:整个测试报告、执行顺序、数据准备、接口校验仍然归 pytest 管理。
- midscene 负责"走流程":把需要真实浏览器操作的步骤封装成智能体脚本。
- 最后的 UI 结果:可以由 midscene 断言,也可以由 pytest 再做一轮兜底校验。
这样做的好处是,团队不需要推翻现有的 CI 流水线和测试资产,只是新增一种"用 AI 智能体执行 UI 流程"的能力。
5.2 pytest侧封装轻量入口的示例
先写一个可复用的 midscene 流程脚本,比如scripts/midscene_flow.ts,它能退出码表示成功或失败。然后 pytest 里用 subprocess 调用它:
import subprocess def test_saucedemo_login_flow(): result = subprocess.run( ["npx", "tsx", "scripts/midscene_flow.ts"], capture_output=True, text=True, ) assert result.returncode == 0, result.stdout + result.stderr这种方式最粗暴,但也最稳:pytest 把 midscene 脚本当黑盒,只需要知道"流程走没走通"。如果团队要求更精细的控制,可以把 midscene 的断言结果输出成 JSON,pytest 读取 JSON 里返回的数据(比如商品价格)再做数值断言。
这里有个很重要的思路:UI 流程交给 AI,数据断言回到传统框架。AI 负责"找到那个按钮并点下去",传统断言负责"价格算得对不对"。各干各最擅长的事,整体稳定性才会高。
5.3 移动端Appium场景:先分清WebView与原生控件
聊到 Appium,这里要泼一点冷水:midscene 这类视觉智能体在移动端 WebView 或 H5 页面可以用,但原生控件的场景不算顺畅。原因很简单:原生控件不在网页 DOM 里,Playwright 管不到,智能体的视觉截图与原生控件没有直接的映射通道。
如果你当前团队以 Appium 做移动端自动化,建议这样考虑:
- 纯原生页面:继续用 Appium,但可以把 AI 用在"语义化断言"上。比如 Appium 取到页面源码和控件树之后,喂给大模型做意图判断:这个页面是否是"登录成功后的首页"。
- WebView / H5 页面:如果应用的业务页面对应的是 H5,可以考虑让 midscene 接管浏览器上下文,用视觉方案跑 WebView 内的流程。
- 混合场景:先用 Appium 切到正确的 WebView context,再把页面交给 Playwright 控制,最后 midscene 接管操作。
移动端这块建议先做小范围验证,不要一上来就把核心 Appium 资产迁移过来。等 WebView 场景跑稳了再逐步扩展。
5.4 渐进式迁移路径与试点范围
从一个真实案例说起。我自己的团队做了一次小的渐进式试点,只用了两个星期,效果就出来了:
- 选一条最痛的链路:从现有的几百条用例里,挑出一条每次发版必做、每次改版必红的链路,比如"创建订单-提交-付款-回显"。
- 用 midscene 重写这条链路:把原来的 XPath 全删掉,改成自然语言任务。
- 并行跑一周:旧的用例继续在 CI 里跑,midscene 脚本也跑,对比两者的稳定性、失败率、维护时间。
- 稳定后替换:新脚本连续一周稳定后,把旧脚本下线,CI 里只留智能体版本。
这个过程不要心急。一次迁移太多链路,你会获得一堆 AI 生成的失败报告和烧掉的 token。从一条链路开始,拿到信心和排错经验后再扩展。
6. 实测中的坑、排错清单与一条判断标准
6.1 从运行日志出发的完整排查链路
智能体脚本报错之后,第一反应不要改代码,先看日志出现在哪一层。我按实际经验整理了这样一个排查顺序:
- 环境变量层:是否报
401或missing api key? 大概率是AI_API_KEY没设置或设置错误。 - 模型调用层:是否返回空结果、JSON 解析失败? 大概率是模型不支持视觉输入,或者请求内容过大被服务商拒绝。
- 规划层:模型有输出,但规划的动作明显不对? 比如"点击登录按钮"规划成了"先点击购物车"。这种情况通常是模型对页面理解有偏差,换更强的多模态模型,或把任务描述写得更具体。
- 执行层:规划正确,但 Playwright 报超时、元素不存在? 看看页面是否真的有弹窗、蒙层、懒加载,把等待时间调大。
- 验证层:操作全部完成,但智能体仍然认为失败? 检查断言措辞是否太模糊,比如"检查首页正常"就不如"断言页面出现 Products 标题"明确。
下面这张排查表,我建议贴到团队的 wiki 里:
| 现象 | 可能原因 | 最快解法 |
|---|---|---|
| 401 / missing api key | 环境变量没配 | 检查AI_API_KEY |
| 模型输出为空 JSON | 模型不支持视觉 | 换多模态模型 |
| 规划动作明显错误 | 任务描述太笼统 | 把目标拆成更明确的子任务 |
| Playwright 点击超时 | 页面弹窗、懒加载 | 调大超时 + 先处理遮挡元素 |
| 断言反复失败 | 期望文案写错 | 对照页面实际文案修改任务描述 |
6.2 实测中高频遇到的五个坑
这一节是我跑断腿换来的经验,逐条说:
坑一:非多模态模型硬上,一跑就挂。第一次我图省事,接了一个只支持文本的模型,结果runTask执行到第一步就报错了。看日志才发现,智能体需要"看图",文本模型根本给不出有效规划。换支持视觉的模型之后一切正常。
坑二:Playwright 浏览器内核忘装。装完playwrightnpm 包直接跑脚本,报错Executable doesn't exist。跑一下npx playwright install chromium就解决。这是新手最容易卡住的一关。
坑三:模型温度配置过高,每次操作结果都不一样。大模型默认的温度参数可能偏高,同一个任务每次规划出来的步骤会漂移。做测试自动化时,一定要把温度调低,理想情况设为 0,让输出尽可能稳定。
坑四:任务描述太笼统。我试过直接说"买一个背包",模型在商品列表页反复横跳,因为它不知道该点哪个商品。改成"点击名称为 Sauce Labs Backpack 的商品图片进入详情页"之后,一次就准了。任务描述越具体,智能体越稳定。
坑五:盲目追求一次跑大量用例。智能体每个任务都要多次截图和模型推理,Token 消耗远高于普通接口调用。跑一百条 UI 用例的成本,可能比跑一万条接口用例还高。务必要把智能体用在刀刃上。
6.3 成本控制:token、超时与重试策略
成本控制是 AI 测试能不能落地的关键。说几个我摸索出来的硬指标:
- 一次简单操作的 Token 消耗:一个"登录"任务,大概会经历 3-5 次截图和模型调用,每次调用消耗几百到上千 Token。一个复杂流程跑下来,可能消耗上万 Token。这个量级在可控范围,但如果你拿它跑全量回归,账单会非常可观。
- 控制重试次数:默认情况下智能体失败后会自动重试。建议设置合理的最大重试次数,比如 2-3 次。再多就是浪费,而且大概率是环境问题而不是智能体问题。
- 把大任务拆小:不要让智能体从"打开浏览器"一路做到"验证数据库"。基础导航和等待用 Playwright 直接写代码完成,只在关键决策点启用智能体。比如
page.goto(url)用代码直接导航,登录、选择商品、验证结果这几步交给智能体。这样既省 Token,又降低模型规划出错的概率。 - 失败现场一定要留证据:无论报告还是截图,都要落盘。因为 AI 智能体的失败往往是概率性的,没有现场截图,你很难判断是模型规划错了,还是页面状态不对。
6.4 引入AI测试智能体的判断标准与个人体会
最后说说我的判断标准,这套标准现在是我们团队决定"要不要用 AI 智能体"的参考:
- 如果团队用例维护成本占测试开发时间超过一半,值得试点。
- 如果某条链路每次改版必红,优先试点。
- 如果页面有大量动态元素、频繁改版的组件,优先试点。
- 如果几千条用例都是"点几下、填个框、看结果"的重复流程,需要谨慎评估成本。
我在实际项目中把一条支付链路交给了 midscene。刚开始的几天,它每天都会红一两次,多数原因是模型对某些弹窗的误判和页面时序问题。后来统一了模型、把温度降到 0、给任务描述加上了明确的边界条件,稳定度明显上去。最明显的变化是:前端改版之后,以前我要花半天改选择器,现在只需要看一遍智能体的报告,确认它通过视觉找到了新的元素位置,然后把旧用例下线。
当然,我也必须说实话:AI 智能体不会解决所有测试问题。数据断言、精确计算、稳定性保障,这些仍然要依靠传统的测试框架和断言手段。它解决的是"定位元素、维护选择器、应对前端改版"这一层最让人头疼的问题,而不是整个测试体系。
如果你正在考虑尝试,我的建议是:不要从全量回归开始,选一条你最痛的核心链路,花一个下午把环境搭起来,跑通上面这个 Demo,然后认真观察一周。AI 测试这件事,只有亲手跑一轮,你才能真切感受到"把维护成本从写选择器转移到人类表达意图"是什么体验。