Midscene.js:基于自然语言与视觉AI的跨平台UI自动化测试实践

发布时间:2026/7/8 17:17:24
Midscene.js:基于自然语言与视觉AI的跨平台UI自动化测试实践 1. 项目概述当UI测试遇上自然语言如果你做过UI自动化测试大概率经历过这样的痛苦花半天时间写一个登录测试脚本结果前端改了个按钮的># 在你的项目目录下初始化并安装Midscene npm init -y npm install midscene如果你需要测试Web端强烈建议同时安装Playwright因为Midscene的Web驱动可以与其无缝集成复用其浏览器实例功能更强大。# 安装Playwright及其浏览器 npm install playwright npx playwright install对于移动端Android/iOS或桌面端测试则需要额外准备相应的平台环境Android需要安装Android SDK并配置好ADB确保设备或模拟器可连接。iOS需要Xcode和appium-WebDriverAgent配置相对复杂。Desktop需要确保被测应用已启动并且操作系统允许辅助功能控制如macOS需在“安全性与隐私”中授权。3.2 编写你的第一个自然语言测试用例让我们从一个最简单的Web测试开始打开Midscene官网并断言页面标题包含“Midscene”。首先创建一个测试文件first-test.js。const { launch, aiAct, aiAssert } require(midscene); (async () { // 1. 启动一个浏览器会话这里使用Playwright驱动 const driver await launch({ platform: web, headless: false // 设为true则不显示浏览器窗口 }); try { // 2. 导航到目标网址 - 这里用的是传统API因为导航不需要视觉识别 await driver.goto(https://midscene.com); // 3. 使用自然语言进行断言检查页面标题 // aiAssert会截图让AI“看”页面并根据你的描述判断断言是否成立 await aiAssert(driver, 页面标题中包含Midscene这个词); // 4. 使用自然语言进行操作点击导航栏的“文档”链接 await aiAct(driver, 点击顶部导航栏中的“文档”链接); // 5. 再次断言确认已跳转到文档页 await aiAssert(driver, 当前页面是文档页面); console.log(测试通过); } catch (error) { console.error(测试失败:, error); } finally { // 6. 关闭驱动释放资源 await driver.close(); } })();运行这个脚本node first-test.js你会看到浏览器自动打开访问官网然后AI会自动找到并点击“文档”链接。整个过程你没有写任何选择器。注意事项第一次运行时Midscene可能会提示你配置AI模型API密钥如OpenAI, DeepSeek等。你需要根据官方文档在环境变量或配置文件中设置MIDSCENE_API_KEY或对应模型的密钥。如果使用其自带的豆包Seed模型则可能无需额外配置但会有调用次数限制。3.3 核心API详解上面的例子引用了两个最常用的API我们来详细拆解一下Midscene.js的核心API设计aiAct(driver, instruction, options?)执行动作。这是最常用的指令。instruction是你用自然语言描述的操作如“在搜索框输入‘自动化测试’并回车”、“点击蓝色的提交按钮”、“向下滚动直到看到加载更多”。options可以指定超时时间、使用的模型、操作前延迟等。aiAssert(driver, instruction, options?)进行断言。用于验证某个状态。instruction是描述预期状态的句子如“登录成功的提示信息应该显示”、“当前页面URL包含‘dashboard’”、“按钮处于禁用状态”。AI会分析截图判断该描述是否成立。aiLocate(driver, instruction, options?)定位元素。当你不需要立即操作只是想获取某个元素的位置信息时使用。它返回一个BoundingBox对象包含x, y, width, height你可以用这个坐标进行更精细的操作比如用传统自动化库去点击。launch(config)启动驱动。这是入口函数根据config.platform‘web’, ‘android’, ‘ios’, ‘desktop’创建并返回对应的驱动实例。配置项决定了使用哪种浏览器、连接哪台设备、指向哪个桌面应用等。这些API的设计哲学是“声明式”的。你关注做什么What而不是怎么做How。这极大地降低了编写测试用例的心智负担和技能门槛产品经理甚至业务人员都有可能参与编写测试场景描述。4. 进阶实战复杂场景与跨平台用例设计4.1 处理复杂交互与条件逻辑真实的测试场景很少是线性的。Midscene.js提供了组合API和编程逻辑来处理复杂情况。场景测试一个购物车功能。添加商品后如果出现“库存不足”的弹窗则点击“知道了”如果添加成功则断言购物车角标数字增加。const { launch, aiAct, aiAssert, aiLocate } require(midscene); (async () { const driver await launch({ platform: web, headless: false }); await driver.goto(https://your-ecom-site.com/product/123); try { // 1. 点击“加入购物车”按钮 await aiAct(driver, 点击“加入购物车”按钮); // 2. 等待一小段时间让页面反应如弹窗出现 await driver.waitForTimeout(2000); // 3. 尝试定位“库存不足”的弹窗。这里使用aiLocate并设置短超时和静默模式。 const outOfStockPopup await aiLocate(driver, 找到显示“库存不足”的提示弹窗, { timeout: 3000, silent: true // 静默模式定位失败不抛异常返回null }).catch(() null); // 进一步捕获可能的错误确保流程继续 if (outOfStockPopup) { // 情况A出现了库存不足弹窗 console.log(检测到库存不足弹窗执行关闭操作。); await aiAct(driver, 点击弹窗上的“知道了”或“关闭”按钮); await aiAssert(driver, 弹窗已经消失); } else { // 情况B没有弹窗假设添加成功 console.log(未检测到异常弹窗假设添加成功。); // 断言购物车图标上的数量角标从0变成了1 // 注意这里需要更精确的指令。假设初始数量是0。 await aiAssert(driver, 页面右上角购物车图标旁边的数字是1); } } catch (error) { console.error(测试流程出错:, error); } finally { await driver.close(); } })();这个例子展示了如何将传统的编程逻辑if-else与Midscene的AI指令结合。aiLocate的silent: true选项在这里是关键它让定位尝试变成一种“探测”行为而非必定成功的断言。4.2 实现真正的跨平台测试用例Midscene.js的威力在于用同一套业务逻辑测试不同平台。假设我们有一个“记事本”应用有Web版、桌面版和移动版我们想测试其核心的“创建-编辑-保存”流程。我们可以抽象出一个通用的测试函数// test-notes-core.js async function testCreateEditSaveNote(driver, platform) { console.log(开始在${platform}平台测试...); // 1. 创建新笔记 await aiAct(driver, 找到并点击“新建笔记”或“”按钮); await aiAssert(driver, 出现了空白的编辑区域或新笔记标题); // 2. 输入内容 await aiAct(driver, 将焦点定位到笔记正文编辑区域); // 这里我们使用driver.type进行精确输入因为aiAct输入长文本可能不稳定 // 先让AI定位到输入框然后直接用底层API输入 const editorBox await aiLocate(driver, 找到主要的文本编辑区域); await driver.click(editorBox.center.x, editorBox.center.y); // 点击定位到的中心点 await driver.type(这是一条由Midscene.js自动创建的测试笔记。时间 new Date().toLocaleString()); // 3. 保存笔记 await aiAct(driver, 点击“保存”或“完成”按钮); await aiAssert(driver, 界面显示保存成功的提示或者编辑区域失去焦点); // 4. 验证笔记列表 await aiAct(driver, 返回或导航到笔记列表页面); // 断言列表中存在包含我们输入特征文字的笔记条目 await aiAssert(driver, 笔记列表中有一条包含“自动创建”字样的笔记); console.log(${platform}平台核心流程测试通过); }然后为不同平台编写启动脚本// test-web.js - 测试Web版 const { launch } require(midscene); const { testCreateEditSaveNote } require(./test-notes-core); (async () { const driver await launch({ platform: web, browserType: chromium, // 使用chromium浏览器 headless: false }); await driver.goto(https://notes-app-web.com); await testCreateEditSaveNote(driver, Web); await driver.close(); })();// test-desktop-mac.js - 测试Mac桌面版 const { launch } require(midscene); const { testCreateEditSaveNote } require(./test-notes-core); (async () { // 启动驱动连接到正在运行的“Notes”桌面应用 const driver await launch({ platform: desktop, desktopApp: Notes, // 应用名称或使用bundleId os: macos }); // 假设应用已经打开在前台 await testCreateEditSaveNote(driver, macOS Desktop); await driver.close(); // 关闭驱动连接不会关闭应用本身 })();// test-mobile-android.js - 测试Android移动版 const { launch } require(midscene); const { testCreateEditSaveNote } require(./test-notes-core); (async () { // 通过ADB连接已开启USB调试的Android设备或模拟器 const driver await launch({ platform: android, deviceName: emulator-5554, // 你的设备序列号 appPackage: com.example.notesapp, // 被测App包名 appActivity: .MainActivity // 启动Activity }); // 驱动会自动启动App await testCreateEditSaveNote(driver, Android); await driver.close(); // 关闭会话 })();可以看到核心业务逻辑testCreateEditSaveNote是完全一致的使用的都是自然语言指令。变化的只是启动驱动时的平台配置。这就是“一套用例多端运行”的雏形。实操心得为了实现最佳跨平台兼容性给AI的指令应尽量语义化、与UI文案相关而不是描述样式。例如用“点击‘保存’按钮”而不是“点击蓝色的圆形按钮”。因为不同平台的UI风格差异巨大但功能文案如“保存”、“确定”、“搜索”通常是相同或相似的。这能极大提高指令在不同平台上的可复用性。5. 集成与CI/CD让AI测试跑起来5.1 与现有测试框架集成Midscene.js本身不绑定任何测试运行器你可以轻松地将其集成到Jest、Mocha、Playwright Test、Cypress等主流测试框架中。以下是一个使用Jest测试框架的示例// notes-app.spec.js const { launch } require(midscene); describe(跨平台记事本应用核心功能, () { let driver; // 每个测试用例开始前启动驱动 beforeEach(async () { driver await launch({ platform: web, headless: true // CI环境下通常无头运行 }); await driver.goto(https://notes-app-test.com); }); // 每个测试用例结束后关闭驱动 afterEach(async () { if (driver) { await driver.close(); } }); test(应能成功创建并保存一条新笔记, async () { const { aiAct, aiAssert } require(midscene); await aiAct(driver, 点击“新建笔记”按钮); await aiAssert(driver, 标题输入框获得焦点); await aiAct(driver, 在正文区域输入“Jest集成测试内容”); await aiAct(driver, 点击“保存”按钮); await aiAssert(driver, 页面显示“保存成功”的提示); }); test(应能搜索到已存在的笔记, async () { const { aiAct, aiAssert } require(midscene); await aiAct(driver, 点击顶部的搜索框); await aiAct(driver, 输入“Jest集成测试”并按下回车); await aiAssert(driver, 搜索结果列表中存在包含该文字的笔记); }); });运行npm test或jest命令这些基于自然语言的测试用例就会像普通单元测试一样执行并生成报告。5.2 在CI/CD流水线中运行在GitHub Actions、GitLab CI、Jenkins等CI/CD环境中运行Midscene测试需要注意以下几点环境准备CI机器需要安装Node.js、浏览器或无头浏览器、以及移动端测试所需的SDK和模拟器。对于桌面端测试CI环境通常难以支持可以考虑将其放在专门的测试机集群中执行。模型API密钥管理将如OPENAI_API_KEY、DEEPSEEK_API_KEY等敏感信息存储在CI/CD系统的Secrets中并通过环境变量注入。截图与报告归档测试失败时截图和详细的AI推理日志是排查问题的关键。Midscene可以与测试框架的报告功能结合将每次操作的截图附加到测试报告中。你也可以配置在失败时自动上传截图到云存储或归档到构建产物中。GitHub Actions 示例配置 (.github/workflows/midscene-test.yml):name: Midscene UI Automation Tests on: [push, pull_request] jobs: test-web: runs-on: ubuntu-latest steps: - uses: actions/checkoutv4 - name: Setup Node.js uses: actions/setup-nodev4 with: node-version: 20 - name: Install dependencies run: npm ci - name: Install Playwright Browsers run: npx playwright install --with-deps chromium - name: Run Midscene Tests env: # 从GitHub Secrets中读取API密钥 MIDSCENE_API_KEY: ${{ secrets.MIDSCENE_API_KEY }} OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} run: npm test -- --testPathPatternweb - name: Upload test artifacts on failure if: failure() uses: actions/upload-artifactv4 with: name: midscene-screenshots path: | test-results/ **/*.png5.3 性能优化与稳定性提升策略在CI中大规模运行视觉AI测试成本和稳定性是两大挑战。策略一分层测试精准使用AI。不要所有测试都用AI。将测试用例分层单元测试/接口测试覆盖核心逻辑和数据流。传统E2E测试使用Playwright/Cypress处理稳定的、选择器明确的流程。AI视觉测试专门用于测试视觉交互、Canvas、动态内容、跨平台UI一致性等传统方法难以覆盖的场景。这样能大幅减少对AI的调用次数。策略二模型分级与缓存。分级为不同重要级别的测试用例配置不同成本的模型。核心冒烟用例用高精度商用模型大量回归用例用开源模型。缓存Midscene支持对视觉模型的定位结果进行缓存。对于UI长期不变的稳定页面首次成功定位后可以将“指令-截图-坐标”的结果缓存起来。下次遇到相同的指令和高度相似的截图时直接使用缓存结果跳过模型调用速度极快且零成本。策略三设置合理的超时与重试。网络波动、AI服务暂时不可用、页面加载慢都可能导致单次失败。在测试框架或Midscene配置中设置合理的超时和自动重试机制如retries: 2可以显著提升流水线的稳定性避免因偶发问题导致的构建失败。6. 避坑指南与常见问题排查在实际项目中踩过不少坑后我总结了一份问题排查清单希望能帮你节省时间。6.1 定位失败AI“找不到”元素这是最常见的问题。指令发出后AI返回“无法定位元素”。检查指令清晰度坏指令“点击那个按钮”“那个”是模糊指代。好指令“点击登录表单中文字为‘提交’的蓝色按钮”或“点击‘登录’按钮”。技巧在指令中包含元素的文本内容和其上下文关系。例如“在用户名输入框下方找到密码输入框”。检查屏幕状态在执行指令前页面是否已经完全加载动态内容如弹窗、下拉列表是否已经出现必要时使用driver.waitForTimeout()或基于传统选择器的等待条件。实操技巧在关键步骤前后手动调用driver.screenshot()并保存图片查看AI“看到”的到底是什么画面。很多时候问题出在页面状态未就绪。调整模型或参数尝试更换更强大的视觉模型如从Qwen-VL切换到豆包Seed。调整confidenceThreshold置信度阈值。默认值可能过高适当调低如从0.9调到0.7可以让AI在不太确定时也尝试操作但可能增加误操作风险。使用options.region参数限定搜索区域减少干扰。例如你知道按钮只在屏幕下半部分就可以指定一个区域框。6.2 操作错误AI“点错了”或“输错了”AI定位到了元素但执行的操作不符合预期。输入文本错误aiAct用于输入长文本时可能会漏字或错字。对于关键的文本输入更可靠的做法是先用aiLocate精确定位输入框然后使用驱动的driver.type(text)或driver.fill(selector, text)方法进行输入。点击位置偏差AI返回的是一个边界框默认会点击该框的中心。如果元素是可点击区域不规则如图标只有一部分可点可能导致点击无效。可以尝试使用options.clickPoint参数指定相对偏移量如{ x: 5, y: 5 }点击左上角。如果元素是固定的首次成功后将其坐标缓存下来后续直接使用缓存坐标点击又快又准。6.3 跨平台指令的适配问题同一句指令在Web上工作在移动端却失败了。平台差异移动端和Web端的UI布局、文案、交互方式可能不同。例如Web端叫“提交”移动端可能是一个“箭头图标”或“完成”。解决方案使用更通用的指令“点击进行登录操作的按钮”。为不同平台编写不同的指令但封装在同一个业务函数里。这需要你对各端UI有一定了解。async function clickLoginButton(driver, platform) { if (platform.includes(mobile)) { await aiAct(driver, 点击屏幕上的“登录”或“Sign In”按钮或者是一个向右的箭头图标); } else { await aiAct(driver, 点击表单下方的“登录”按钮); } }利用Midscene的“Skills”或自定义Agent将针对特定平台的操作逻辑封装成更高级的指令如loginWithCredentials(username, password)在这个Skill内部处理平台差异。6.4 成本与速度控制AI调用是按Token或次数计费的速度也比本地操作慢。启用缓存如前所述这是降本增效的最有效手段。确保在测试配置中开启缓存功能。批量操作对于一连串操作如填写表单可以考虑用一个复杂的指令完成而不是分多个aiAct调用。例如“在注册表单中依次在‘用户名’框输入‘testuser’在‘邮箱’框输入‘testexample.com’在‘密码’框输入‘Password123’然后点击‘注册’按钮”。但这需要模型有较强的规划能力成功率可能低于分步操作需要权衡。监控与预算定期查看AI服务商的控制台监控Token消耗设置预算警报。在非关键路径或大量回归测试中优先使用性价比高的开源模型。视觉驱动的UI自动化测试是一个正在快速发展的领域Midscene.js提供了一个非常前沿且实用的实现。它并非要完全取代传统的基于选择器的测试而是作为一种强大的补充专门攻克那些传统方法难以应对的“硬骨头”。将两者结合构建一个分层、智能、可维护的自动化测试体系或许是未来高质量UI测试的发展方向。