行业资讯

AI视觉自动化测试实战:Midscene.js跨平台测试解决方案

发布时间:2026/7/1 21:26:07
AI视觉自动化测试实战:Midscene.js跨平台测试解决方案 1. 项目概述为什么我们需要AI视觉自动化测试如果你是一名前端、测试或者全栈开发者最近肯定没少被“AI”、“自动化”这些词刷屏。传统的自动化测试无论是用Selenium、Cypress还是Playwright本质上都是基于DOM元素定位的。我们得写一堆>const { midscene } require(midscene-js); const { expect } require(chai); // 可以使用任何断言库 describe(用户登录流程, () { it(应该能用正确密码登录, async () { // 1. 导航到页面 await midscene.goto(https://your-app.com/login); // 2. 视觉定位元素并操作 // 找到看起来像“用户名输入框”的区域并点击它 await midscene.click(username_field.png); // 传入一个参考图片模板 await midscene.type(testuser); // 找到看起来像“密码输入框”的区域 await midscene.click(password_field.png); await midscene.type(securepassword123); // 找到并点击“登录”按钮 await midscene.click(login_button.png); // 3. 视觉断言等待并验证“欢迎信息”出现在屏幕上 const isWelcomeVisible await midscene.waitFor(welcome_message.png, { timeout: 5000 }); expect(isWelcomeVisible).to.be.true; // 或者使用OCR进行文本断言 const pageText await midscene.getText(); // 获取页面所有文本 expect(pageText).to.include(欢迎回来testuser); }); });你会发现整个脚本里没有出现任何#login-btn或.form-input之类的选择器。所有交互都基于你提供的图片模板如login_button.png。这些模板就是你的“视觉定位器”。注意图片模板的创建是关键。你需要从应用程序界面中截取干净、标准的元素图像作为模板。最好在UI设计稳定后从测试环境中截取并确保截取的图片背景相对纯净特征明显。3. 环境搭建与项目初始化从零开始的第一步理论说再多不如动手跑一遍。下面我将带你完成一个完整的Midscene.js项目搭建目标是对一个简单的待办事项TodoWeb应用进行跨平台Web的视觉自动化测试。3.1 基础环境准备首先确保你的开发环境已经就绪Node.jsMidscene.js是一个Node.js库需要Node.js环境建议版本16或以上。可以去官网下载安装。包管理器npm或yarn随Node.js一同安装。一个代码编辑器VS Code、WebStorm等均可。打开终端创建一个新的项目目录并初始化mkdir midscene-todo-test cd midscene-todo-test npm init -y3.2 安装核心依赖Midscene.js本身是一个高层抽象库它需要底层的浏览器自动化驱动来工作。这里我们选择Playwright作为驱动因为它对现代Web特性支持好且自带浏览器二进制文件无需单独安装Chrome驱动。npm install midscene-js playwright这条命令会安装midscene-js库以及Playwright。安装Playwright时它会自动下载Chromium、Firefox和WebKit的二进制文件这可能需要一些时间。实操心得在国内网络环境下Playwright的二进制下载可能会很慢甚至失败。可以尝试设置镜像源PLAYWRIGHT_DOWNLOAD_HOSThttps://npmmirror.com/mirrors/playwright npx playwright install chromium或者使用cnpm进行安装。如果团队测试需要覆盖多种浏览器可以考虑只安装最常用的Chromium以加快初始化速度。3.3 编写你的第一个视觉测试脚本假设我们有一个在本地运行的Todo应用http://localhost:3000。我们的第一个测试用例是打开应用添加一个新的待办事项。第一步创建视觉模板在项目根目录创建一个images文件夹。这是存放所有视觉模板的地方。打开你的Todo应用截取以下关键元素的图片保存到images目录下new_todo_input.png- 用于输入新事项的文本框区域。add_button.png- “添加”按钮。todo_item.png- 一个已存在的待办事项条目的示例用于后续验证。截图技巧使用浏览器开发者工具的截图功能可以精确截取某个DOM节点避免多余背景。确保截图清晰元素完整。对于按钮可以截取包含部分周围空白区域的图增加匹配容错率。第二步创建测试文件在项目根目录创建test文件夹并在其中创建第一个测试文件todo.spec.js// test/todo.spec.js const { midscene } require(midscene-js); describe(Todo App 视觉自动化测试, () { // 在每个测试用例开始前启动浏览器并打开页面 beforeEach(async () { await midscene.launch({ headless: false }); // headless: false 表示打开浏览器窗口方便调试 await midscene.goto(http://localhost:3000); }); // 在每个测试用例结束后关闭浏览器 afterEach(async () { await midscene.close(); }); it(应该能成功添加一个新的待办事项, async () { // 1. 视觉定位“新事项输入框”并点击获得焦点 await midscene.click(./images/new_todo_input.png); // 2. 输入文本 await midscene.type(学习Midscene.js视觉测试); // 3. 视觉定位并点击“添加”按钮 await midscene.click(./images/add_button.png); // 4. 验证尝试在屏幕上寻找一个包含“学习Midscene.js视觉测试”文本的待办事项条目 // 这里我们结合视觉模板和OCR文本验证 const todoItemFound await midscene.find(./images/todo_item.png, { text: 学习Midscene.js视觉测试, // OCR文本过滤条件 confidence: 0.8, // 视觉匹配置信度阈值0.8表示80%相似度即认为匹配 }); // 使用Node内置的assert进行简单断言 if (!todoItemFound) { throw new Error(未找到新添加的待办事项); } console.log(测试通过成功添加并找到了待办事项。); }); });第三步运行测试在package.json的scripts字段中添加一条命令{ scripts: { test: node -r ./test/todo.spec.js } }然后运行npm test如果一切顺利你会看到一个浏览器窗口自动打开访问你的Todo应用自动完成输入和点击操作并在控制台输出成功信息。踩坑记录第一次运行时最常见的错误是“找不到模板图片”。请务必检查图片路径是否正确。./images/是相对于测试文件执行时的当前工作目录通常是项目根目录。如果遇到匹配失败可以尝试将headless设为false观察脚本运行过程看它是否在正确的位置进行了点击。调整confidence阈值默认通常是0.9。如果UI有抗锯齿或细微渲染差异可以适当调低至0.7或0.8。重新截取模板图片确保与测试运行时页面的视觉状态完全一致包括主题、字体等。4. 核心API与高级用法详解掌握了基础流程后我们来深入看看Midscene.js提供的一些核心API和高级功能这些能让你编写出更强大、更稳定的测试脚本。4.1 关键API操作指南midscene对象提供了多种与屏幕交互的方法以下是最常用的几个click(imagePath, options)点击与模板图片匹配的区域。options可以指定offset: {x, y}在匹配区域的相对位置点击这对于点击大图标的特定部分很有用。options中的confidence可以覆盖全局设置。doubleClick(imagePath, options)/rightClick(imagePath, options)双击和右键点击。type(text, options)输入文本。它会将焦点设置到当前活动元素通常由前一个click触发然后模拟键盘输入。options可以设置delay来模拟真人打字速度。hover(imagePath, options)鼠标悬停在元素上。用于触发下拉菜单、Tooltip等。dragAndDrop(sourceImage, targetImage)将一个视觉元素拖拽到另一个视觉元素的位置。这是测试拖放交互的神器。find(imagePath, options)在屏幕中查找一个元素返回其坐标和尺寸信息但不执行操作。常用于存在性断言。waitFor(imagePath, options)等待某个元素出现在屏幕上直到超时。这是处理异步加载和页面跳转的关键。options中可以设置timeout默认30秒和interval检查间隔。getText(options)使用OCR获取当前屏幕上的所有文本。你可以结合find先定位一个区域然后对这个区域的截图进行OCR获取更精确的文本。screenshot(options)截取当前屏幕或指定区域的图片。可用于测试失败时自动保存现场证据或者动态生成视觉模板。4.2 处理动态内容与等待策略视觉测试最大的挑战之一是“等待”。页面加载、数据获取、动画效果都可能导致元素没有立即出现。显式等待waitFor这是最主要的策略。在任何一个需要与元素交互的操作之前先等待它出现。// 等待登录按钮出现最多等10秒 await midscene.waitFor(./images/login_btn.png, { timeout: 10000 }); await midscene.click(./images/login_btn.png);结合OCR的动态文本断言有时你要验证的文本是动态生成的如订单号、时间戳。// 假设我们支付后会出现一个包含订单号的确认信息 await midscene.waitFor(./images/order_confirmation_area.png); const confirmationText await midscene.getText({ roi: ./images/order_confirmation_area.png }); // roi: Region of Interest只识别该区域的文本 // 使用正则表达式匹配动态订单号 const orderNumberMatch confirmationText.match(/订单号[:]\s*([A-Z0-9]{10})/); expect(orderNumberMatch).to.not.be.null; console.log(创建的订单号为${orderNumberMatch[1]});处理加载状态很多应用在加载数据时会显示一个旋转的Loading图标。一个好的模式是等待Loading图标消失再继续操作。// 点击查询后等待Loading图标出现又消失 await midscene.click(./images/search_button.png); await midscene.waitFor(./images/loading_spinner.png, { timeout: 3000 }); await midscene.waitForDisappear(./images/loading_spinner.png, { timeout: 10000 }); // 假设有waitForDisappear方法或其变体 // 然后继续验证查询结果如果库没有提供waitForDisappear你可以用waitFor配合timeout和反向逻辑来实现。4.3 跨平台测试配置实战Midscene.js的“跨平台”威力在于它能用同一套视觉逻辑对接不同的驱动。下面演示如何配置同一个测试套件分别针对Web和移动端浏览器运行。我们需要修改项目结构使用配置文件来管理不同平台的环境。第一步创建配置文件midscene.config.js// midscene.config.js module.exports { projects: [ { name: Desktop Chrome, use: { driver: playwright, // 使用Playwright驱动 browser: chromium, // 使用Chromium浏览器 viewport: { width: 1920, height: 1080 }, headless: true, // 在CI环境中设为true以节省资源 }, }, { name: Mobile Safari, use: { driver: playwright, browser: webkit, // 模拟Safari (iOS) device: iPhone 13, // Playwright提供的设备描述符 isMobile: true, viewport: { width: 390, height: 844 }, // iPhone 13的视口 headless: true, }, }, // 理论上可以添加Android配置使用appium驱动 // { // name: Android App, // use: { // driver: appium, // platformName: Android, // deviceName: emulator-5554, // app: ./path/to/your/app.apk, // }, // }, ], // 全局设置 screenshotOnFailure: true, // 测试失败时自动截图 screenshotPath: ./test-results/screenshots/, templateImagePath: ./images/, // 视觉模板的基准路径 };第二步改造测试脚本以使用配置我们的测试脚本需要能读取配置并针对不同的project运行。这通常需要测试运行器如Jest、Mocha的支持。这里我们假设使用一个简单的自定义运行逻辑或者直接利用Midscene.js可能提供的多项目运行能力具体需查阅其最新文档。核心思想是测试脚本中的操作APIclick,type等是通用的但启动环境midscene.launch会根据配置变化。你可能需要编写一个setup函数来根据环境变量选择配置。// test/todo.crossplatform.spec.js const config require(../midscene.config.js); // 假设我们通过环境变量 PROJECT_NAME 来指定运行哪个项目 const projectName process.env.PROJECT_NAME || Desktop Chrome; const projectConfig config.projects.find(p p.name projectName); if (!projectConfig) { throw new Error(未找到项目配置: ${projectName}); } const { midscene } require(midscene-js); describe(Todo App 测试 - ${projectName}, () { beforeEach(async () { // 根据配置启动特定环境 await midscene.launch(projectConfig.use); const startUrl projectConfig.use.isMobile ? http://localhost:3000/m : http://localhost:3000; // 假设有移动端专属URL await midscene.goto(startUrl); }); afterEach(async () { await midscene.close(); }); it(跨平台添加待办事项, async () { // 注意移动端和桌面端的模板图片可能需要不同因为UI布局可能不同。 // 我们可以根据平台选择不同的模板路径 const templatePrefix projectConfig.use.isMobile ? ./images/mobile/ : ./images/desktop/; await midscene.click(${templatePrefix}new_todo_input.png); await midscene.type(测试任务 ${projectName}); await midscene.click(${templatePrefix}add_button.png); const found await midscene.find(${templatePrefix}todo_item.png, { text: 测试任务 ${projectName}, confidence: 0.7, // 移动端渲染差异可能更大置信度调低 }); expect(found).to.be.ok; }); });第三步运行不同平台的测试通过环境变量来控制运行哪个配置# 测试桌面端 PROJECT_NAMEDesktop Chrome npm test # 测试移动端模拟器 PROJECT_NAMEMobile Safari npm test高级技巧对于真正的移动端原生App测试你需要将驱动切换到Appium并配置相应的Desired Capabilities设备UDID、App包名等。Midscene.js的视觉识别部分仍然工作但启动和会话管理的方式会不同。你需要准备两套视觉模板一套用于iOS一套用于Android因为它们的UI设计规范通常不同。5. 集成与CI/CD让视觉测试在团队中跑起来个人本地运行测试只是第一步要让AI视觉测试创造最大价值必须将其集成到持续集成/持续部署CI/CD流水线中。这样每次代码提交都能自动运行测试及时发现问题。5.1 与常见测试运行器集成Midscene.js本身不绑定测试运行器你可以选择你熟悉的任何Node.js测试框架。Jest流行的全能测试框架内置断言、Mock和覆盖率。npm install --save-dev jest在jest.config.js中配置测试文件匹配模式。你的midscene测试文件可以和其他单元测试放在一起Jest会并行或顺序执行它们。Mocha Chai非常灵活的组合。Mocha组织测试用例Chai提供丰富的断言语法。我们前面的例子就是这种风格。npm install --save-dev mocha chai在package.json中设置test: mocha test/**/*.spec.js。Cucumber如果你喜欢行为驱动开发BDD可以用Cucumber编写Gherkin语法特性文件然后用Midscene.js来实现底层步骤定义让非技术人员也能参与测试用例设计。5.2 配置GitHub Actions自动化流水线下面是一个简单的GitHub Actions工作流配置示例用于在每次推送到主分支或发起拉取请求时自动运行Midscene.js视觉测试。在项目根目录创建.github/workflows/visual-tests.ymlname: Visual Regression Tests with Midscene.js on: push: branches: [ main ] pull_request: branches: [ main ] jobs: test: runs-on: ubuntu-latest # 使用GitHub托管的Linux虚拟机 steps: - name: Checkout code uses: actions/checkoutv3 - name: Setup Node.js uses: actions/setup-nodev3 with: node-version: 18 # 使用与本地一致的Node版本 - name: Install dependencies run: npm ci # 使用ci命令确保依赖锁一致 - name: Install Playwright Browsers run: npx playwright install chromium --with-deps # 在CI中只安装必要的Chromium - name: Run Midscene.js Visual Tests run: npm test env: CI: true # 告诉测试运行器在CI环境中 HEADLESS: true # 强制无头模式没有GUI # 可以在这里传递其他环境变量如测试服务器的URL TEST_BASE_URL: ${{ secrets.TEST_BASE_URL }} # 从GitHub Secrets读取测试环境地址 - name: Upload test artifacts on failure if: failure() # 只有测试失败时才运行此步骤 uses: actions/upload-artifactv3 with: name: test-failure-screenshots path: test-results/ # 上传我们在配置中设置的截图目录这个工作流做了以下几件事检出代码。安装Node.js和项目依赖。安装Playwright的Chromium浏览器这是Midscene.js的驱动依赖。运行测试脚本npm test。我们通过环境变量CItrue和HEADLESStrue确保在无头模式下运行。关键一步如果测试失败将test-results/目录里面包含了失败时的屏幕截图上传为工作流制品。这对于调试视觉匹配失败至关重要你可以直接下载这些图片看看测试时的屏幕状态到底是什么样的。CI环境避坑指南无头模式CI服务器没有图形界面必须设置headless: true。视口和分辨率CI环境的屏幕分辨率可能与本地不同可能影响视觉匹配。建议在配置中明确设置固定的viewport大小。字体渲染差异Linux服务器上的字体渲染可能与macOS/Windows不同可能导致OCR文本识别或细微的视觉匹配失败。可以考虑在CI环境中安装统一的字体包或者对非关键文本匹配适当降低置信度。测试数据准备确保CI流水线能访问到一个稳定的、干净的测试环境通过TEST_BASE_URL指定。这个环境的数据应该在每次测试运行前被重置。5.3 测试结果管理与报告单纯的“通过/失败”输出不够直观。我们可以集成一些报告生成工具Allure Report生成非常美观的交互式测试报告可以展示测试步骤、截图、甚至屏幕录制如果配置了。npm install --save-dev allure-commandline在测试脚本中可以在关键步骤后使用midscene.screenshot()并将图片路径附加到Allure报告中。HTML Reporter像mochawesome或jest-html-reporter这样的库可以生成简单的HTML报告方便在CI流水线完成后查看。将报告生成步骤添加到CI配置中并上传报告文件作为制品团队就能方便地查看每次测试运行的详细结果和历史趋势。6. 常见问题、调试技巧与最佳实践即使有了强大的工具在实际项目中还是会遇到各种问题。下面是我在多个项目中总结出的经验。6.1 视觉匹配失败原因与解决方案这是最常见的问题。脚本报错“找不到图片模板XXX”。问题现象可能原因解决方案完全匹配不到1. 模板图片路径错误。2. 模板图片与屏幕当前内容差异巨大如UI已改版。3. 元素尚未加载出来异步问题。1. 检查路径使用绝对路径或相对于项目根目录的路径更稳妥。2. 更新模板图片。建议将模板图片纳入版本控制UI改版时同步更新。3. 在操作前添加waitFor确保元素出现。间歇性匹配失败1. 动态内容如时间、计数器导致区域像素变化。2. 动画效果如淡入、旋转导致截图瞬间状态不同。3. 跨平台/浏览器渲染细微差异。1. 避免截取包含动态文本的区域作为模板。使用ROI感兴趣区域只截取静态的图标或按钮形状部分。2. 在操作前等待动画结束或使用waitFor并设置较高的timeout。3.适当降低confidence阈值如从0.9调到0.7。为不同平台准备不同的模板集。匹配到错误位置1. 页面上存在多个相似元素如多个相同图标。2. 模板图片特征不够独特。1. 使用更精确的模板或结合上下文。例如先定位一个父区域再在该区域内查找子元素。2. 截取更具区分度的区域或者使用find返回所有匹配项然后通过位置或OCR文本进行二次筛选。调试技巧开启可视化调试在调试时设置headless: false并可以启用Midscene.js的高亮模式如果支持让它用红框标出识别到的区域。保存失败截图如前所述在CI中配置失败时自动截图。本地运行时也可以在catch块中手动调用midscene.screenshot()保存当前页面。打印匹配信息find或waitFor方法通常会返回匹配的坐标和置信度。把这些信息打印出来有助于判断是没找到还是找到了但位置不对。6.2 测试稳定性提升策略模板管理策略版本化模板图片是测试代码的一部分必须和测试脚本一起进行版本控制。黄金模板在UI稳定的版本如每次大版本发布前截取一套“黄金模板”作为基准。差异化模板为不同的主题深色/浅色、不同的平台iOS/Android/Web维护不同的模板目录。编写鲁棒的测试用例多用waitFor少用sleep永远不要使用固定的sleep等待而应使用waitFor等待特定视觉状态出现或消失。原子化操作每个测试步骤应尽可能独立。一个步骤只做一件事并验证一件事。清理测试数据测试用例应该能够独立运行不依赖特定状态。在beforeEach或afterEach钩子中清理测试产生的数据如删除刚创建的待办事项。处理非确定性界面对于模态框、提示信息等可能随机出现的内容编写专门的关闭或处理函数。使用try-catch包裹非核心的辅助操作比如关闭一个可能不存在的欢迎提示。6.3 何时使用和不使用Midscene.js非常适合的场景跨平台UI回归测试验证核心业务流程在不同端表现一致。视觉回归测试检测意外的UI样式改动虽然专业工具如Percy更擅长此道但Midscene.js也可实现。无法/难以通过DOM定位的界面测试游戏、Canvas/WebGL应用、三维编辑器、 legacy系统。黑盒测试与用户体验流测试从真实用户视角验证端到端流程。需要谨慎或不适用的场景极致的执行速度视觉识别比DOM操作慢如果测试套件非常庞大执行时间可能成为瓶颈。更适合作为核心冒烟测试或关键路径测试。像素级精确断言如果需要验证1像素的偏移或精确的色值Midscene.js不是最佳选择应考虑专门的视觉差分工具。纯API或单元测试这些应该由更轻量级的测试如Jest、Supertest覆盖。高度动态的验证码或安全组件这些设计就是为了防止自动化强行测试意义不大且容易失败。我个人在实践中通常会建立一个混合测试策略单元测试覆盖核心逻辑集成测试覆盖API基于DOM的E2E测试覆盖大部分Web交互而Midscene.js则作为补充专门用于上述它擅长的场景特别是跨平台一致性验证和复杂图形界面的测试。这样既能保证测试覆盖率又能控制整体测试套件的执行效率和维护成本。