Midscene.js:视觉驱动UI自动化,告别脆弱选择器

Midscene.js:视觉驱动UI自动化,告别脆弱选择器 1. 项目概述当UI自动化不再依赖选择器作为一名在测试自动化和前端工程领域摸爬滚打了十多年的老手我经历过UI自动化测试从Selenium的“石器时代”到Playwright/Cypress这类现代框架的演进。但一个核心痛点始终如影随形选择器Selector的脆弱性。无论是CSS选择器、XPath还是各种>mkdir midscene-demo cd midscene-demo npm init -y npm install midscene/sdk接下来你需要配置模型API密钥。Midscene.js本身不提供模型你需要去相应的AI服务商平台获取。这里以性价比高的qwen为例在阿里云灵积平台可获取在项目根目录创建.env文件# .env QWEN_API_KEYyour_qwen_api_key_here # 如果你也用豆包可以添加 # DOUBAO_API_KEYyour_doubao_api_key_here然后创建一个基础的配置文件midscene.config.js用于声明默认模型和平台// midscene.config.js export default { defaultModel: qwen3.7-plus, // 默认使用qwen模型 platforms: { web: { provider: playwright, // 使用playwright作为web驱动 headless: false // 调试时设为false可以看到浏览器操作 }, desktop: { provider: os-native // 使用操作系统原生方式驱动桌面 } } };3.2 核心API实战自动化一个Web应用登录流程假设我们要自动化测试一个Web登录页面。我们使用Playwright作为底层驱动结合Midscene.js的视觉能力。// test-web-login.js import { launch } from midscene/sdk; import { chromium } from playwright; // 需要安装 playwright: npm install playwright (async () { // 1. 启动Playwright浏览器 const browser await chromium.launch({ headless: false }); const context await browser.newContext(); const page await context.newPage(); await page.goto(https://example.com/login); // 2. 创建Midscene会话绑定到当前页面 const session await launch({ driver: page, // 将Playwright page对象作为驱动 model: qwen3.7-plus }); // 3. 使用aiAct执行自然语言流程 try { // 场景1完整的登录流程 await session.aiAct(在用户名输入框输入“test_user”); await session.aiAct(在密码输入框输入“password123”); await session.aiAct(点击“登录”按钮); // 等待登录后页面跳转或加载 await page.waitForTimeout(2000); // 场景2使用aiAssert进行视觉验证 const assertResult await session.aiAssert(页面中应该显示“欢迎回来test_user”或“登录成功”的提示); if (assertResult.success) { console.log(✅ 登录成功断言通过); } else { console.log(❌ 登录后未找到预期文本可能登录失败。); } // 场景3处理更复杂的元素比如一个SVG图标按钮 await session.aiAct(点击页面右上角的用户头像图标); await session.aiAct(在下拉菜单中点击“退出登录”); } catch (error) { console.error(自动化执行失败:, error); } finally { // 4. 清理资源 await session.close(); await browser.close(); } })();代码解读与注意事项驱动绑定launch函数中的driver参数至关重要。对于Web我们传入了Playwright的page对象这样Midscene.js就能通过这个页面上下文来截图和注入执行脚本。指令的精确性自然语言指令虽然灵活但也需要一定的精确性。“用户名输入框”比“输入框”更好。如果页面有多个输入框可以增加上下文描述如“在左侧表单的用户名输入框”。等待与稳定性AI模型处理需要时间页面反应也需要时间。在关键操作后如点击登录适当使用page.waitForTimeout或等待特定元素出现能极大提高脚本稳定性。Midscene.js的API内部已有重试机制但合理的等待是良好实践。错误处理一定要用try...catch包裹核心自动化逻辑。aiAct可能会因为元素未找到、指令歧义等原因抛出异常良好的错误处理有助于调试和生成清晰的测试报告。3.3 挑战桌面端自动化一个桌面软件操作桌面自动化是Midscene.js的亮点。我们以自动化电脑上的“记事本”应用为例完成打开、输入、保存操作。// automate-desktop-notepad.js import { launch } from midscene/sdk; (async () { // 1. 启动Midscene桌面会话。不需要额外驱动它会自动捕获整个屏幕。 const session await launch({ platform: desktop, // 指定平台为桌面 model: qwen3.7-plus }); // 2. 使用系统命令或aiAct打开记事本这里以Windows为例macOS/Linux指令不同 const { exec } require(child_process); exec(notepad.exe); // 打开记事本 await session.waitFor(3000); // 等待记事本窗口弹出 // 3. 开始视觉自动化 try { // 聚焦记事本窗口假设它是当前最前窗口 await session.aiAct(点击标题为“无标题 - 记事本”的窗口); // 输入文本 await session.aiAct(在记事本的编辑区域输入“这是由Midscene.js自动输入的文字。Hello, World!”); // 打开“文件”菜单 await session.aiAct(点击顶部菜单栏的“文件(F)”菜单); await session.waitFor(500); // 给菜单下拉一点时间 // 选择“另存为”选项 await session.aiAct(在下拉菜单中点击“另存为(A)...”选项); // 等待“另存为”对话框弹出 await session.waitFor(2000); // 在文件名输入框中输入 await session.aiAct(在“文件名(N):”旁边的输入框中输入“midscene_test.txt”); // 点击保存按钮 await session.aiAct(点击“保存(S)”按钮); console.log(✅ 桌面记事本自动化流程执行完毕); } catch (error) { console.error(桌面自动化失败:, error); // 可以在这里截图保存现场便于调试 await session.screenshot(error_state.png); } finally { await session.close(); // 可选关闭记事本 exec(taskkill /f /im notepad.exe); } })();踩坑实录桌面自动化的核心挑战窗口管理与聚焦桌面环境窗口众多。指令如“点击保存按钮”可能作用于错误的窗口。最佳实践是先用aiAct明确聚焦目标窗口通过窗口标题再进行后续操作。Midscene.js的视觉定位是基于当前屏幕截图的确保目标窗口在屏幕前端且未被遮挡。系统差异菜单栏文字“File” vs “文件”、快捷键、甚至对话框结构在不同操作系统和语言版本下都不同。编写跨平台桌面脚本时指令应尽可能通用或针对不同平台准备不同的指令集。可以利用Midscene的配置系统来管理平台相关的变量。等待时机桌面应用的反应速度差异很大。session.waitFor是必不可少的。对于模态对话框弹出等场景可以结合aiAssert进行等待例如while (!(await session.aiAssert(‘另存为对话框已出现’)).success) { await wait(500); }。权限问题在macOS和Linux上控制其他应用可能需要辅助功能权限。首次运行脚本时系统会提示务必在系统设置中授权。4. 高级应用Skills技能与集成生态Midscene.js不仅仅是一个库它正在构建一个围绕“视觉驱动自动化”的生态。其中最值得关注的是“Skills”功能。4.1 什么是Skills你可以把Skills理解为预封装、可复用的自动化脚本或能力模块。例如一个“用户登录Skill”可能封装了针对你公司特定登录页面的视觉操作逻辑。更强大的是这些Skills可以通过Midscene CLI被外部的AI编程智能体如GitHub Copilot、Cursor或你自建的Agent直接调用。这意味着你可以将复杂的、稳定的业务流程如“数据报表导出”、“每日系统健康检查”封装成Skill。然后你只需要对AI智能体说“请运行‘日报导出’Skill”它就能通过Midscene CLI自动执行这一系列视觉操作无需再编写或生成详细的逐步指令。4.2 如何创建与使用一个自定义Skill假设我们要为内部管理系统创建一个“审批通过”Skill。首先在项目内创建一个Skills定义文件如skills.yaml:# skills.yaml skills: - name: approve_request description: 在OA系统中批准第一条待办请求 steps: - action: aiAct args: [点击侧边栏的‘待办事项’菜单] - action: waitFor args: [1000] - action: aiAct args: [点击列表中第一个‘待处理’的请求卡片] - action: waitFor args: [1500] - action: aiAct args: [点击详情页下方的‘同意’按钮] - action: aiAssert args: [页面应显示‘审批完成’的绿色提示]然后你可以在你的Node.js脚本中通过Midscene SDK加载并执行这个Skillimport { launch, SkillRunner } from midscene/sdk; import { chromium } from playwright; import fs from fs; import yaml from js-yaml; // 需要安装 js-yaml (async () { const browser await chromium.launch(); const page await browser.newPage(); await page.goto(https://your-oa-system.com); const session await launch({ driver: page }); // 加载Skill定义 const skillDefinition yaml.load(fs.readFileSync(./skills.yaml, utf8)); const approveSkill skillDefinition.skills.find(s s.name approve_request); // 使用SkillRunner执行 const runner new SkillRunner(session); await runner.run(approveSkill); await session.close(); await browser.close(); })();更进一步你可以将这个Skill暴露给CLI让AI智能体调用# 通过CLI运行特定Skill npx midscene skill run approve_request --target https://your-oa-system.comSkills的价值它将不稳定的视觉指令可能因UI微调而需要修改封装在了一个黑盒中。外部调用者无需关心内部是点击了“确定”按钮还是“OK”按钮只需知道“approve_request”这个功能。当UI变更时你只需要更新这一个Skill定义所有调用它的智能体或脚本都无需改动极大地提升了可维护性。4.3 与现有测试框架集成你不需要完全抛弃Playwright或Jest。Midscene.js可以作为一个强大的“增强插件”集成到你的现有测试体系中。// 在Playwright测试中使用Midscene进行视觉断言 import { test, expect } from playwright/test; import { launch } from midscene/sdk; test(复杂的视觉交互测试, async ({ page }) { await page.goto(/complex-chart-page); const session await launch({ driver: page }); // 传统Playwright断言检查某个数据属性 await expect(page.locator([data-testidchart-container])).toBeVisible(); // Midscene视觉断言检查图表上是否“显示了一个红色的上升趋势线” const visualAssertResult await session.aiAssert(图表中应包含一条明显的红色上升趋势线); expect(visualAssertResult.success).toBeTruthy(); // 用自然语言操作一个Canvas图表 await session.aiAct(在图表图例区域点击“2024年”系列); await session.aiAct(将图表下方滑动条拖到最右边); await session.close(); });这种混合模式让你既能享受传统框架的稳定性和速度对于结构稳定的部分又能利用Midscene突破那些传统框架无法触及的“视觉测试”盲区。5. 性能优化、问题排查与最佳实践在实际企业级应用中稳定性和性能是关键。以下是我在多个项目中总结的经验。5.1 性能优化策略截图区域优化默认全屏截图数据量大传输和处理慢。如果操作区域固定可以指定截图区域。// 仅截取屏幕中央800x600区域进行处理 await session.aiAct(点击登录按钮, { screenshotRegion: { x: 100, y: 100, width: 800, height: 600 } });模型调用批处理对于连续多个不依赖上一步UI状态的操作可以考虑合并指令。// 不佳连续调用每次都要截图、调用模型 await session.aiAct(输入用户名); await session.aiAct(输入密码); // 更佳合并为一个指令如果界面在输入用户名后不会突变 await session.aiAct(在用户名框输入“admin”然后在密码框输入“123456”);缓存与复用对于静态或很少变化的界面元素其视觉特征在单次会话中是稳定的。Midscene.js内部有缓存机制但也要注意在页面发生重大跳转后可能需要清除缓存。降级方案对于核心、高频且元素稳定的操作可以混合使用传统选择器。先用视觉驱动完成初始定位和探索然后将稳定的元素坐标或选择器记录下来后续直接使用提升速度。5.2 常见问题排查清单当你遇到自动化失败时可以按以下清单逐项排查问题现象可能原因排查步骤与解决方案aiAct超时或报“元素未找到”1. 指令描述歧义。2. 目标元素未加载/不可见。3. 屏幕分辨率/缩放导致视觉特征变化。4. 模型“看”错了。1.检查指令使用更精确的描述加入上下文如“在蓝色表单中的登录按钮”。2.手动等待在操作前增加page.waitForSelector(Web) 或session.waitFor。3.检查截图使用await session.screenshot(debug.png)保存失败时的截图人工检查目标是否在图中、是否清晰。4.切换模型尝试使用不同的视觉模型如从qwen换到豆包Seed。操作执行了但没效果如点击无效1. 元素被遮挡弹窗、遮罩层。2. 坐标计算偏移系统缩放125%。3. 需要特殊交互如双击、长按。1.检查遮挡截图查看是否有弹窗。可用aiAct(‘关闭提示弹窗’)先清理。2.处理缩放Midscene.js通常能处理但极端情况需在启动配置中设置scaleFactor。3.使用正确API对于非点击操作使用aiDoubleClick,aiLongPress等专用API。桌面自动化时操作了错误窗口目标窗口未聚焦或被其他窗口遮挡。1.先聚焦窗口在任何操作前先用aiAct(‘点击[窗口标题]窗口’)确保目标在前台。2.指定窗口区域如果多个相似窗口在指令中加入更多特征如“点击那个正在闪烁的聊天窗口”。脚本在CI/CD中不稳定1. 无头模式/虚拟帧缓冲差异。2. 网络或模型API不稳定。3. 环境差异字体、主题。1.本地复现先在CI环境的docker镜像中本地运行调试。2.增加重试在关键步骤外包裹重试逻辑并设置合理的超时和间隔。3.环境标准化确保测试环境的系统主题、字体、分辨率与CI一致。使用固定版本的浏览器/模拟器。模型API调用费用激增1. 脚本存在死循环频繁调用。2. 截图分辨率过高。3. 未使用性价比策略。1.添加防护在循环逻辑中设置最大迭代次数。2.降低截图质量在非必要情况下配置screenshotQuality: ‘low’。3.模型策略对非关键路径使用低成本模型如qwen核心路径用高精度模型如豆包。5.3 企业级最佳实践指令库管理不要将自然语言指令硬编码在脚本里。建立一个中心化的“指令库”或“描述词库”例如用JSON文件管理不同页面元素的标准化描述。当UI变更时只需更新这个库。// element-descriptions.json { loginPage: { usernameInput: 顶部的用户名输入框旁边有‘账号’标签, passwordInput: 密码输入框显示为圆点, submitButton: 蓝色的‘登录’按钮 } }视觉基线对比对于重要的UI状态断言除了aiAssert可以结合传统的视觉回归测试工具如pixelmatch将成功状态的截图作为基线后续运行中对比像素差异更客观。分层测试策略不要试图用Midscene.js覆盖所有测试。将其用于跨平台核心业务流程验证、无DOM/Canvas的交互测试、探索性测试自动化。单元测试和API集成测试仍用更快速、稳定的传统方法。监控与报告充分利用Midscene.js生成的可视化报告。报告里会逐步展示每一步的截图、指令和AI的识别结果这是调试和审计的宝贵资料。将其集成到你的测试报告平台如Allure中。我个人在将Midscene.js引入团队项目时最大的体会是它改变了我们设计自动化用例的思维方式。我们从“如何找到这个元素”变成了“如何描述这个功能”。初期需要投入时间与团队一起定义清晰的描述规范并建立对AI偶尔“犯傻”的容忍度和应对机制。但一旦跑顺它对于快速覆盖多端UI、应对频繁的UI迭代以及自动化那些“选择器禁区”的价值是传统工具完全无法比拟的。它不是一个完美的银弹但在正确的场景下它是一个能够显著提升效率和覆盖率的革命性工具。