Midscene.js 纯视觉UI自动化:截图即定位,把“修选择器“从测试维护里删掉
E2E 测试最大的维护成本从来不是写用例,而是修选择器:前端重构一次,data-testid 换一茬,CI 红一片。DOM 方案的另一半问题是"看不见"——icon-only 按钮、<canvas> 渲染的图表、无语义标注的自定义控件,选择器和无障碍树都摸不到,更别说 Android/iOS 原生界面和跨域 iframe。Midscene.js(ByteDance web-infra-dev 出品,GitHub 14.5K+ stars,MIT,最新 v1.10.12 于 2026-08-13 发布)换了一条路:元素定位只依赖截图,不读 DOM。测试描述用自然语言,模型直接在图上看"搜索框在哪",返回坐标执行点击输入。定位与渲染栈解耦,跨 Web/Android/iOS/HarmonyOS/桌面端共用同一套 API 和心智模型。
定位原理:截图 → 多模态模型 → 坐标
纯视觉定位的调用链极短,一次 aiAct 大致是这样:
页面截图(viewport) ──► 多模态模型(Default role) ──► 定位结果 JSON ──► 执行层(click/input/hover)
▲ │
└──────────── 动作后截图,进入下一步(多步任务循环)◄──────────────────────┘
模型返回的不是"哪个 DOM 节点",而是一组元素坐标和动作参数。这一步有两个关键设计:
- 动作与数据提取分离。动作(点哪里、输入什么)永远只看截图,保证跨端一致;数据提取和页面理解(
aiQuery/aiAssert)可以按需带上 DOM,弥补纯视觉在精确文本读取上的短板。 - token 消耗与 DOM 规模无关。传统 AI 自动化把整个 DOM/无障碍树塞进 prompt,页面元素越多 token 越贵;纯视觉方案的 token 只取决于截图分辨率和任务复杂度。这是它敢用于大型后台系统的底气。
代价是模型门槛:不是任意 LLM 都能做 GUI 定位,官方限定了一批 GUI 能力稳定的多模态模型——Qwen3.x、Doubao-Seed-2.1、GLM-4.6V、gemini-3.5-flash,以及可自托管的开源 UI-TARS。
与主流方案放一起看,取舍更清楚:
| 维度 | 传统 Playwright 选择器 | DOM/无障碍树 + AI | Midscene 纯视觉 |
|---|---|---|---|
| 定位依据 | CSS/XPath/testid | DOM 结构 + 标注截图 | 截图本身 |
| 重构抗性 | 低,选择器随 UI 失效 | 中,结构变化仍会挂 | 高,只要"看着没变"就能跑 |
| canvas/原生端/跨域 iframe | 不支持 | 部分不支持 | 支持(人眼可见即可定位) |
| 断言对象 | DOM 节点存在性 | 节点 + 部分视觉 | 用户实际看到的状态(颜色/高亮/布局) |
| token 成本 | 无 | 随 DOM 规模膨胀 | 只随截图分辨率变化 |
| 模型依赖 | 无 | 需要 LLM | 需要指定的多模态模型 |
一个常见误解是"纯视觉 = 完全不用 DOM"。实际是动作层不依赖 DOM,aiQuery/aiAssert 这类数据提取和页面理解任务仍可按需注入 DOM 上下文,官方 API 里保留了显式开关。
模型角色分工:Default / Planning / Insight
单模型能跑通全部场景,但复杂任务可以拆三个角色各干各的:
| 角色 | 职责 | 典型场景 |
|---|---|---|
| Default(必配) | 元素定位(Locate)+ 兜底所有任务 | 点击、输入、hover |
| Planning(可选) | 复杂目标的多步规划、分支决策 | "下单流程走完,遇到弹窗就关掉" |
| Insight(可选) | 数据提取、断言、页面理解 | aiQuery 结构化抽取、aiAssert 视觉校验 |
多模型组合会显著增加延迟和 token 消耗——规划模型和定位模型各推理一轮。我的建议:先单模型跑通,只有断言质量或复杂任务规划不达标时再引入 Insight/Planning,别一开始就堆配置。
代码实践:五分钟接入 Playwright 套件
import { chromium } from 'playwright';
import { PlaywrightAgent } from '@midscene/web/playwright';
import 'dotenv/config';
const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));
(async () => {
const browser = await chromium.launch({ headless: false });
const page = await browser.newPage();
await page.goto('https://www.ebay.com');
await sleep(3000);
const agent = new PlaywrightAgent(page);
// 自然语言动作
await agent.aiAct('type "Headphones" in the search box, then hit Enter');
await agent.aiWaitFor('there is at least one headphone product in the list');
// 结构化数据提取
const items = await agent.aiQuery(
'{ title: string, price: number }[], the headphone products in the list',
);
console.log('in stock:', items);
// 视觉断言:校验的是用户"看到"的状态
await agent.aiAssert('There is a category filter on the left side');
await browser.close();
})();
模型配置走环境变量,四种组合即可切换模型:
export MIDSCENE_MODEL_BASE_URL="https://openrouter.ai/api/v1"
export MIDSCENE_MODEL_API_KEY="your-api-key"
export MIDSCENE_MODEL_NAME="qwen/qwen3.7-plus"
export MIDSCENE_MODEL_FAMILY="qwen3"
跑完会生成 ./midscene_run/report/<id>.html 报告,每一步动作、查询、断言都带截图可回放,定位 AI 干了什么是靠它,不是靠日志。和现有 Playwright/Vitest 套件是平级集成——aiAssert 抛错即用例失败,不改变原有断言体系。
不想写代码时,同一条自然语言指令可以直接写成 YAML 剧本,交给 agent 模式或 CI 执行:
- aiAct: 'open the search box and type "Headphones", then hit Enter'
- aiWaitFor: 'the result list shows at least one headphone product'
- aiQuery: '{ title: string, price: number }[], headphone products in the list'
- aiAssert: 'the page shows a category filter on the left side'
声明式剧本的好处是测试即文档,产品、测试、开发三方都能 review 用例意图,也方便在 CI 里按环境变量切换目标环境。
踩坑与实测建议
模型选择是最大的坑。 纯视觉定位对模型要求苛刻,用不支持的多模态模型会出现"看得见但点不准"——模型描述对了元素,坐标却偏了几个像素。生产环境建议:国内链路用 Qwen3.x/Doubao,数据敏感场景用自托管的 UI-TARS 7B 跑在本地 GPU 上(延迟可控,无出网合规问题);追求极致效果再上 gemini。先在小流量页面上跑 20 条用例对比定位准确率再定。
token 成本按"步"算。 每步动作 = 一张截图 + 一次模型推理,一个 10 步的流程就是 10 次调用。压成本的手段:减少不必要的 aiWaitFor 轮询(它每次轮询都调模型)、能用普通 Playwright waitForSelector 的等待就别用 AI 等待、长列表分页后只对首屏做 aiQuery。
确定性要靠"可观测性 + 重试"兜底。 模型输出偶尔会解析失败,v1.10.6 起支持配置响应格式和解析失败自动重试;v1.10.10 的 record model calls、v1.10.11 的请求 tracing headers 把每次模型调用的入参出参都留痕。遇到 flaky,先看报告里模型到底"看"到了什么——80% 的 flaky 是页面弹窗/懒加载遮挡,不是模型问题,在动作前加一个"关掉弹窗"的 aiAct 比调模型参数有效。
明确纯视觉的边界。 精确文本断言(如金额必须等于 ¥1,234.56)别依赖视觉模型读字,用 aiQuery 带 DOM 提取后做数值断言;校验"按钮是否高亮、布局是否错位"这类视觉状态才是 aiAssert 的主场。混用策略是常态:动作走纯视觉、关键数据校验走 DOM,各取所长。
收尾
Midscene.js 的价值不在"AI 写测试",而在把测试的维护成本结构改了:选择器维护从 O(每次重构) 变成 O(0),跨端复用从"每端一套框架"变成"一套自然语言描述"。进阶方向:YAML agent 模式把测试写成声明式剧本、Midscene Skills 接 OpenClaw 做自主探索测试、以及把同一套 aiAct 直接搬到 Android/iOS/桌面端。适合先拿一个页面复杂度高、选择器频繁失效的业务模块试点,用报告里的定位成功率数据决定是否全量铺开。
更多推荐


所有评论(0)