Midscene.js:基于视觉与AI的下一代UI自动化测试实战指南 1. 项目概述当AI“看见”你的界面如果你和我一样在UI自动化测试这个领域摸爬滚打了几年那你一定对“选择器”这三个字又爱又恨。爱它是因为它给了我们一个精准定位元素的“锚点”恨它是因为这个锚点太脆弱了——产品经理心血来潮改个样式开发重构一下组件结构我们辛辛苦苦写好的测试用例就立刻“红”给你看。维护成本高、无法覆盖Canvas等非DOM元素、对原生应用和跨域iframe束手无策……这些都是传统基于页面结构DOM或无障碍树的自动化框架绕不开的痛点。最近一个名为Midscene.js的开源项目进入了我的视野它提出了一种截然不同的思路视觉驱动。简单来说它不关心你的页面内部是什么结构它只“看”屏幕截图然后像真人测试工程师一样根据你描述的自然语言指令去理解和操作界面。这个概念让我眼前一亮因为它直击了传统自动化测试最核心的脆弱性问题。Midscene.js 将自己定位为一个用于视觉驱动UI测试与自动化的开源SDK它覆盖了Web、移动端Android/iOS/HarmonyOS、桌面端甚至游戏或复杂应用中的canvas场景。这意味着只要是人眼能看到并能操作的界面理论上它都能处理。这篇教程就是带你从零开始快速上手这个AI驱动的自动化测试新框架。无论你是想为你的Web应用寻找更健壮的回归测试方案还是想自动化操作某个没有源码的桌面软件Midscene.js 都可能是一个值得深入探索的工具。我会结合官方文档和我自己的实践体验拆解它的核心原理、手把手教你完成第一个自动化脚本并分享在实战中可能遇到的“坑”和应对技巧。2. 核心设计思路为什么是“视觉驱动”在深入代码之前我们必须先理解 Midscene.js 的底层逻辑。这决定了我们该如何有效地使用它以及它能解决哪些传统框架解决不了的问题。2.1 传统自动化框架的“结构依赖”困境我们熟悉的 Selenium、Playwright、Cypress 乃至 Appium其核心工作原理都是与应用程序的“结构层”进行交互。对于Web应用这个结构层是DOM文档对象模型对于移动应用可能是 Accessibility Tree无障碍树或 UIAutomator/XCUITest 提供的控件树。框架通过CSS选择器、XPath、ID等定位器找到目标元素然后执行点击、输入等操作。这种方式的优势是精准、快速。但它的致命弱点在于强耦合。一旦前端代码重构选择器就可能失效。更棘手的是很多界面元素在结构层是“隐形”的纯视觉元素一个用div和背景图实现的图标按钮在DOM里可能只是一个没有任何文本或ARIA标签的div。Canvas/WebGL整个游戏或复杂图表绘制在canvas标签内其内部绘制的按钮、滑块在DOM中根本不存在。跨域iframe出于安全限制主页面脚本通常无法直接访问跨域iframe内部的DOM结构。原生桌面应用没有DOM的概念传统Web自动化框架无从下手。当自动化脚本因为一个按钮颜色从#337ab7改成#0d6efd而失败时尽管功能完全正常或者因为一个重要的图表无法被测试时测试的维护成本和价值就会受到严峻挑战。2.2 Midscene.js 的“视觉感知”路径Midscene.js 选择了一条“另类”的道路绕过结构层直接模拟人类用户的感知-决策-执行过程。感知通过截图获取当前界面的完整视觉状态。决策将截图和你的自然语言指令如“点击登录按钮”一起提交给一个多模态大模型。这个模型经过海量图像和文本数据训练能够理解图像内容并推断出指令中描述的元素在图像中的位置通常以坐标边界框的形式返回。执行SDK 接收到模型返回的坐标信息将其转换为对应平台浏览器、移动设备、桌面的底层输入事件如鼠标点击、触摸事件、键盘输入并执行。这个过程完全基于像素不依赖于任何内部实现细节。只要这个按钮在屏幕上看起来像个按钮并且人能认得出来AI模型就有很大概率也能定位到它。这带来了几个革命性的优势抗重构性强只要UI的视觉外观和功能逻辑没有变无论背后的HTML结构、CSS类名、组件框架如何变化自动化脚本都能继续工作。覆盖范围广Canvas、视频控件、桌面应用界面、甚至物理设备的屏幕只要能截图就能被自动化。验证更贴近用户你可以用自然语言进行视觉断言例如“检查成功提示框是否显示为绿色并包含‘操作成功’文字”这比检查某个隐藏的>await aiAct(登录邮箱找到最新一封来自“系统通知”的邮件将其标记为已读然后返回收件箱);这种方式开发速度极快适合探索性测试或一次性任务。但其不确定性也最高AI的规划可能出错且难以调试和稳定回归。风格二工作流风格这是更接近传统编程的、可控性更高的方式。你将流程拆解为明确的步骤只在需要“视觉智能”的环节如定位、判断调用AI其他逻辑用代码控制。// 1. AI定位并点击登录按钮 await aiTap(登录按钮); // 2. 传统方式输入用户名密码更稳定 await page.fill(#username, testuser); await page.fill(#password, password123); // 3. AI判断验证码图片中的数字并输入 const captchaText await aiQuery(string, the 4-digit number in the captcha image); await page.fill(#captcha, captchaText); // 4. AI点击提交 await aiTap(提交按钮); // 5. AI断言登录成功后的欢迎语 await aiAssert(页面上显示了包含用户名的欢迎语例如“你好testuser”);这种方式结合了AI的灵活性和代码的精确性是构建稳定、可维护的自动化测试套件的推荐做法。Midscene.js 的官方文档也强烈建议对于关键业务流程采用这种风格。3. 环境搭建与快速开始理论讲完了我们动手来跑通第一个例子。我会以最常用的 Web 浏览器自动化为例。3.1 环境准备你需要准备以下几样东西Node.js 环境Midscene.js 是一个Node.js SDK建议安装最新的LTS版本如18.x或20.x。可以通过node -v检查。一个多模态大模型的API密钥这是Midscene.js的“眼睛”和“大脑”。它支持多种模型对于初学者我推荐使用DeepSeek-V3或Qwen2.5-VL它们在视觉理解和性价比上表现不错且有方便的在线API。DeepSeek去其官网注册在控制台创建API Key。Qwen在阿里云灵积平台开通服务并获取API Key。一个Chromium内核的浏览器如Chrome、Edge或Playwright自带的Chromium。注意模型API调用会产生费用但新用户通常有免费额度。开始实验前请了解相关模型的计价方式并设置好用量限额避免意外开销。3.2 初始化项目与安装打开终端创建一个新的项目目录并初始化。mkdir midscene-demo cd midscene-demo npm init -y安装 Midscene.js 核心包以及 PlaywrightMidscene 可以与 Playwright 无缝集成利用其强大的浏览器控制能力。npm install midscene playwright同时你需要安装 Playwright 的浏览器内核。npx playwright install chromium3.3 配置模型API密钥安全地管理你的API密钥。推荐使用环境变量。在项目根目录创建一个.env文件记得将其加入.gitignore# .env DEEPSEEK_API_KEY你的_DeepSeek_API_Key # 或者使用Qwen # QWEN_API_KEY你的_Qwen_API_Key然后安装dotenv包来读取环境变量。npm install dotenv3.4 编写第一个自动化脚本创建一个名为first-test.js的文件。// first-test.js require(dotenv).config(); // 加载环境变量 const { chromium } require(playwright); const { Midscene } require(midscene); (async () { // 1. 启动浏览器 const browser await chromium.launch({ headless: false }); // 设置为 false 以便观察 const context await browser.newContext(); const page await context.newPage(); // 2. 创建 Midscene 代理连接到当前页面 const agent new Midscene(); await agent.attach(page); // 将Midscene绑定到Playwright的page对象 // 3. 配置AI模型 await agent.setConfig({ model: { // 这里以DeepSeek-V3为例 provider: deepseek, model: deepseek-chat, apiKey: process.env.DEEPSEEK_API_KEY, }, }); // 4. 导航到测试页面这里以百度为例因为其UI稳定且众所周知 await page.goto(https://www.baidu.com); await page.waitForLoadState(networkidle); // 等待页面基本加载完成 console.log(页面加载完毕开始AI操作...); try { // 5. 让AI在搜索框里输入内容 // aiType 方法让AI找到描述的元素并输入文本 await agent.aiType(搜索框, Midscene.js 自动化测试); console.log(已输入搜索关键词); // 等待一下让输入反应在界面上 await page.waitForTimeout(1000); // 6. 让AI点击“百度一下”按钮 // aiTap 方法让AI找到描述的元素并点击它 await agent.aiTap(百度一下按钮); console.log(已点击搜索按钮); // 7. 等待搜索结果加载 await page.waitForSelector(#content_left, { state: visible }); await page.waitForTimeout(2000); // 再给点时间渲染 // 8. 让AI断言搜索结果中是否包含预期内容 // aiAssert 方法让AI根据视觉判断一个陈述是否为真 const assertionResult await agent.aiAssert(搜索结果页面中显示了与“Midscene.js”相关的链接); console.log(视觉断言结果, assertionResult ? 通过 : 未通过); // 9. 可选让AI获取一些文本信息 // aiQuery 方法让AI根据视觉回答一个自然语言问题 const firstResultTitle await agent.aiQuery(string, the title text of the first search result link); console.log(AI提取的第一个结果标题是, firstResultTitle); } catch (error) { console.error(自动化执行过程中出错, error); } finally { // 10. 稍作停留后关闭浏览器 await page.waitForTimeout(3000); await browser.close(); } })();3.5 运行并观察在终端运行你的脚本node first-test.js如果一切配置正确你将看到一个浏览器窗口自动打开并导航到百度首页。AI会自动将光标定位到搜索框可能不是百分之百精确但大概率成功并输入“Midscene.js 自动化测试”。AI会点击“百度一下”按钮。页面跳转到搜索结果页。控制台会打印出断言结果和AI提取的第一个标题。第一次看到浏览器被一段自然语言指令驱动时感觉还是挺奇妙的。你可能会注意到AI点击和输入的位置有时会有细微偏差这取决于模型对当前截图的理解精度。这就是视觉驱动的特点之一。4. 核心API与实战技巧解析成功运行了“Hello World”之后我们来深入看看 Midscene.js 提供的核心API以及如何在实战中更有效地使用它们。4.1 核心API方法详解Midscene Agent 对象提供了多个以ai为前缀的方法它们是构建自动化的积木。aiTap(description)/aiClick(description)核心点击操作。description是你对目标元素的自然语言描述越具体越好。例如‘红色的“提交”按钮’就比‘按钮’更精确。对于Web它会模拟鼠标点击对于移动端则是触摸事件。aiType(description, text)在指定的输入元素中输入文本。它会先定位元素然后点击聚焦再输入文本。对于需要清空再输入的场景可以结合aiClear方法。aiAssert(description)视觉断言。描述一个你期望为真的视觉状态AI会返回布尔值。例如‘弹窗已经出现并且标题是“操作成功”’。这是进行视觉回归测试的关键。aiQuery(instruction)向AI提问关于当前屏幕的问题并获取结构化的回答。instruction里可以指定返回类型如‘string, the total price shown in the cart badge’字符串购物车徽章中显示的总价或‘number, the count of unread notifications’数字未读通知的数量。这个方法非常强大可以从非结构化的界面中提取信息。aiAct(instruction)让AI自主规划并执行一系列操作来完成一个高级指令。如前所述威力大但不确定性高。aiScroll(direction, description?)滚动页面。direction可以是‘up’,‘down’,‘left’,‘right’。可以可选地提供一个description来指定在哪个可滚动容器内滚动。aiWaitFor(description, options?)等待某个描述的元素出现或消失。这比固定的page.waitForTimeout更智能。4.2 提升稳定性的实战技巧视觉自动化并非银弹其成功率受截图质量、模型理解能力、界面动态变化的影响。以下是几个提升脚本稳定性的关键技巧1. 描述语的艺术具体化、唯一化、上下文化差的描述‘点击按钮’屏幕上可能有一堆按钮。 好的描述‘点击登录表单下方蓝色的“立即登录”按钮’。 更好的描述结合上下文‘在用户名和密码输入框下方点击那个蓝色的“登录”按钮’。 你可以利用界面上已有的、稳定的文本作为参照物来定位目标。2. 结合传统定位器混合使用不要排斥选择器。对于稳定且容易定位的元素如固定的导航栏、有固定ID的输入框使用 Playwright 的原生定位方式 (page.click(‘#submit’)) 速度更快、成本为零、稳定性100%。Midscene 与 Playwright 是完美互补的。将AI用于难以定位或动态生成的视觉元素将传统方法用于稳定元素。3. 实施重试与等待策略网络延迟、动画效果、模型偶尔的误判都可能导致单次操作失败。为关键的AI操作添加重试逻辑。async function robustAiTap(description, maxRetries 3) { for (let i 0; i maxRetries; i) { try { await agent.aiTap(description); return; // 成功则退出 } catch (error) { console.warn(第 ${i 1} 次尝试点击“${description}”失败:, error.message); if (i maxRetries - 1) { await page.waitForTimeout(1000); // 等待一秒再重试 } else { throw new Error(点击“${description}”失败已达最大重试次数); } } } }同时在关键界面状态变化后如提交表单后使用page.waitForLoadState(‘networkidle’)或aiWaitFor(‘成功提示信息’)来等待而不是写死等待时间。4. 利用缓存提升速度与降低成本每次调用AI模型都需要传输截图和支付API费用。Midscene 支持缓存AI的规划定位结果。对于相对稳定的界面首次运行后后续运行可以直接使用缓存的坐标速度极快且零成本。你可以在配置中开启await agent.setConfig({ cache: { enabled: true, // 缓存目录可以提交到仓库实现团队共享 dir: ‘./.midscene-cache’, }, });5. 生成并利用可视化报告每次运行Midscene 都可以生成一份详细的HTML报告记录每一步的截图、AI指令、执行结果和模型返回的定位框。这对于调试失败的用例至关重要。确保在CI/CD流水线中收集这些报告。// 在脚本结束时可以手动触发报告生成 await agent.generateReport(‘./test-results/midscene-report.html’);5. 集成到现有测试体系Midscene.js 不是一个要取代你现有测试框架的工具而是一个强大的增强插件。它可以轻松集成到 Playwright Test 或 Vitest/Jest 这样的测试运行器中。5.1 与 Playwright Test 集成假设你已有一个基于 Playwright Test 的测试项目。// tests/visual-search.spec.js const { test, expect } require(‘playwright/test’); const { Midscene } require(‘midscene’); require(‘dotenv’).config(); test.describe(‘视觉增强的搜索功能测试’, () { let agent; test.beforeEach(async ({ page }) { // 为每个测试页面创建一个Midscene代理 agent new Midscene(); await agent.attach(page); await agent.setConfig({ model: { /* ... 你的模型配置 ... */ }, cache: { enabled: true, dir: ‘./cache’ }, }); await page.goto(‘https://your-app.com’); }); test.afterEach(async () { // 每个测试后生成报告 const testInfo test.info(); const reportPath ./test-results/${testInfo.title.replace(/s/g, ‘-’)}.html; await agent.generateReport(reportPath); }); test(‘应该能通过视觉定位并完成商品搜索’, async ({ page }) { // 使用Playwright传统方式导航到搜索页稳定 await page.click(‘nav a[href“/search”]’); // 使用AI在复杂的搜索框输入可能是一个自定义组件 await agent.aiType(‘顶部的大型搜索框 placeholder是“搜索商品”’, ‘无线耳机’); await agent.aiTap(‘搜索框右边的放大镜图标按钮’); // 等待并混合断言传统视觉 await expect(page).toHaveURL(/search/); const hasResults await agent.aiAssert(‘页面显示了商品列表并且第一个商品包含“耳机”字样’); expect(hasResults).toBeTruthy(); }); });这样你就可以在同一个测试套件中自由混用精准的结构化断言和灵活的视觉断言。5.2 构建数据驱动的视觉测试你可以将测试数据和对应的视觉断言描述放在外部文件如JSON或YAML中实现数据驱动。// test-cases/search-cases.json [ { “searchTerm”: “笔记本电脑”, “expectedVisualClue”: “筛选栏出现了“品牌”和“价格区间”选项” }, { “searchTerm”: “#$%”, “expectedVisualClue”: “页面顶部显示了红色的“搜索关键词无效”错误提示” } ]// 在测试中读取并循环执行 const testCases require(‘./search-cases.json’); for (const tc of testCases) { test(搜索“${tc.searchTerm}”的视觉反馈, async ({ page, agent }) { await agent.aiType(‘搜索框’, tc.searchTerm); await agent.aiTap(‘搜索按钮’); const result await agent.aiAssert(tc.expectedVisualClue); expect(result).toBeTruthy(); }); }6. 常见问题与排查指南在实际使用中你肯定会遇到一些挑战。下面是我总结的一些典型问题及其解决方法。6.1 AI操作失败或定位不准这是最常见的问题。症状aiTap点击了错误的位置或根本没找到元素aiAssert返回了错误的布尔值。排查步骤检查截图首先查看Midscene生成报告中的那一步截图。模型“看到”的和你看到的是否一致有没有可能页面还在加载中元素尚未渲染确保在操作前使用了足够的等待。审查描述你的描述是否足够清晰、无歧义尝试添加更多上下文信息。例如不要只说“设置图标”而说“页面右上角、头像旁边的齿轮形状设置图标”。简化界面如果界面元素非常密集或动态可以尝试先通过传统操作如点击选项卡进入一个更简单的子页面再进行AI操作。尝试不同模型Midscene支持多种模型。如果某个模型对特定界面风格如极简主义、拟物化理解不好可以换一个试试。在配置中切换provider和model。调整配置参数有些模型配置项如temperature创造性可以调低使其输出更确定。查看对应模型的配置文档。6.2 执行速度慢或API费用高每次AI调用都有网络延迟和成本。解决方案务必开启缓存这是提升后续运行速度、降为零成本的最有效手段。确保cache.enabled为true。减少不必要的AI调用能用page.click(‘#id’)解决的绝不用aiTap。将AI用于真正需要视觉智能的环节。使用更轻量的模型对于简单的定位任务不一定需要最强大的模型。可以尝试响应更快、价格更便宜的模型如gemini-2.0-flash-exp或qwen2.5-vl-7b如果支持。批量操作对于列表操作尽量使用aiQuery一次性获取所有信息然后用代码循环处理而不是对每个列表项都调用一次AI。6.3 在CI/CD流水线中运行失败无头环境、分辨率、字体可能与本地开发环境不同。解决方案统一环境在CI中使用与本地相同版本的操作系统、浏览器和字体包。考虑使用Docker容器。设置固定的视口大小在测试开始时使用page.setViewportSize({ width: 1920, height: 1080 })设置固定的浏览器窗口大小。视觉定位对分辨率敏感。处理无头模式有些网页在无头模式下渲染的字体或布局略有差异。如果问题只在CI出现可以尝试在CI中也以“headed”模式运行需要虚拟帧缓冲区如Xvfb或者针对CI环境调整AI描述的措辞。管理API密钥将API密钥存储在CI系统的安全变量中而非代码里。6.4 报告文件太大每次运行都包含大量截图报告文件可能高达几十MB。解决方案选择性生成报告只在测试失败时生成详细报告。可以在test.afterEach钩子中根据testInfo.status判断。清理旧报告在CI脚本中配置只保留最近N次的运行报告。使用外部存储将报告上传到云存储如S3或专门的测试报告平台并提供链接而不是保存在CI工作区。7. 进阶应用场景探索当你熟悉了基础操作后Midscene.js 还能打开一些更有想象力的场景。7.1 自动化非Web应用这是Midscene的强项。通过其提供的SDK你可以连接到Android设备、iOS模拟器/真机、甚至Windows/macOS的桌面应用。Android/iOS你需要先通过ADB或WebDriverAgent等方式将设备界面暴露出来。Midscene 提供了midscene/android和midscene/ios包来简化连接。之后你就可以用同样的aiTap(‘微信图标’)、aiType(‘密码输入框’, ‘123456’)来操作手机APP了非常适合做跨设备的兼容性测试或重复性APP操作。桌面应用通过midscene/pc包你可以捕获整个屏幕或特定窗口的截图并进行操作。这可以用来自动化一些没有API的遗留桌面软件的操作流程。7.2 视觉回归测试Visual Regression Testing虽然Midscene的aiAssert是语义层面的断言但你也可以将其用于基础的视觉回归。例如在关键页面更新后让AI断言“页面的整体布局和主要板块没有发生异常错位”。对于更精确的像素级对比可以结合传统的视觉差分工具如pixelmatch但Midscene提供了更高层次的、基于理解的“视觉正确性”判断。7.3 与AI Agent结合实现自主测试这是更前沿的用法。Midscene 支持通过MCPModel Context Protocol或Skills系统将你的应用界面操作能力“暴露”给一个更通用的AI Agent例如一个使用Claude或GPT-4的Agent。你可以告诉这个Agent“去测试一下我们购物车的结算流程。”Agent会利用Midscene提供的“技能”如浏览商品、加入购物车、去结算自主规划并执行一整套测试用例并最终给你一份测试报告。这代表了未来自动化测试的一个可能方向由AI自主生成并执行测试场景。从我个人的实践来看Midscene.js 并非要取代你现有的所有测试而是填补了那些“传统自动化难以触及”的空白区域。它特别适合用于测试视觉密集型或Canvas渲染的应用如游戏、数据可视化、设计工具。为没有源码或无法修改的第三方应用/网站编写自动化脚本。构建对UI重构不敏感的冒烟测试套件降低维护成本。快速验证新功能的视觉表现和核心用户流。它的学习曲线并不陡峭尤其是如果你已有JavaScript和Playwright的经验。主要的挑战在于如何编写精准的提示词描述以及如何设计混合策略AI传统来平衡稳定性与灵活性。开始时可以从一个小而具体的场景入手比如只用一个aiTap来点击一个用传统方式很难定位的浮动按钮感受其工作模式再逐步扩大使用范围。记住它是一把锋利的瑞士军刀用在合适的场景下才能发挥最大威力。