1. 项目概述当UI自动化不再依赖DOM大家好我是老张一个在测试开发领域摸爬滚打了十多年的老兵。这些年从Selenium到Playwright从Appium到Cypress我几乎把市面上主流的UI自动化框架都用了个遍。但有一个痛点始终如影随形页面结构DOM/View Hierarchy的脆弱性。产品经理一个心血来潮的改版开发同学一次看似无害的重构就能让精心维护的自动化脚本瞬间“瘫痪”测试同学就得连夜加班去更新那些脆弱的选择器XPath、CSS Selector。更别提那些纯图标按钮、Canvas渲染的游戏界面、或者跨域的iframe对于传统基于结构的自动化工具来说简直就是“盲区”。最近一个名为Midscene.js的开源项目进入了我的视野它提出了一种截然不同的思路视觉驱动Vision-Driven。简单说它不关心你的页面底层是什么结构它只“看”屏幕截图然后像真人一样根据你描述的自然语言去理解和操作界面。这个概念让我眼前一亮因为它直击了传统UI自动化的命门。今天我就结合自己的实操经验和大家详细拆解如何用Midscene.js仅需三步构建一套真正健壮、跨平台的AI自动化测试方案。无论你是测试工程师、前端开发者还是对RPA机器人流程自动化感兴趣的朋友这套方案都可能为你打开一扇新的大门。2. 核心思路拆解为什么是“视觉驱动”在深入三步构建法之前我们必须先理解Midscene.js赖以生存的“视觉驱动”理念以及它为何能解决我们多年的痛点。2.1 传统结构依赖的“阿喀琉斯之踵”传统的UI自动化无论是Web的Playwright还是移动端的Appium其核心工作原理都是通过浏览器开发者工具或移动端无障碍服务获取应用界面的结构化描述DOM树、View Hierarchy。自动化脚本通过编写选择器在这个结构树中定位元素然后执行点击、输入等操作。这套模式的弊端非常明显极度脆弱前端框架升级、UI组件库更换、甚至只是开发修改了一个div的class名都可能导致选择器失效。测试脚本的维护成本Test Maintenance Cost常年居高不下。覆盖不全对于非标准控件、完全由Canvas/WebGL渲染的内容如图表、游戏、缺乏无障碍标签的纯图标按钮结构树中要么没有对应节点要么节点信息缺失自动化工具“看不见”它们。跨域隔离Web中的跨域iframe其内部的DOM对于外层页面的脚本是不可直接访问的这给自动化带来了巨大障碍。验证片面传统断言Assertion通常只验证某个元素是否存在、属性值是否正确但无法验证这个元素在用户眼中实际呈现的效果是否正确比如颜色偏差、布局错乱、图像加载失败等视觉问题。2.2 Midscene.js的破局之道像人一样“看”和“做”Midscene.js绕开了结构树选择了一条更接近人类本能的方式输入当前屏幕的截图 一句自然语言指令如“点击登录按钮”。核心调用一个强大的多模态大模型Multimodal LLM例如Qwen-VL、GLM-4V等。这些模型经过海量图像和文本的联合训练具备惊人的视觉理解和推理能力。过程模型“看”着截图理解你的指令在图像中定位目标如找到“登录按钮”的位置并规划出操作如计算点击的坐标。输出Midscene.js接收模型返回的坐标和操作类型通过底层驱动如Playwright、ADB执行真实的点击操作。这个过程带来了几个革命性的优势抗变化能力强只要“登录按钮”在屏幕上看起来还是个按钮并且人能认出来无论它的底层HTML标签从button变成了div还是class名从btn-primary改成了submit-btnMidscene.js都能找到并点击它。脚本的稳定性大幅提升。无所不“见”只要是屏幕上像素能呈现的内容无论是Canvas图表、游戏UI、桌面应用窗口还是移动端原生控件模型都能尝试理解。真正实现了“所见即可测”。真正的视觉验证你可以直接让AI判断“这个弹窗的标题是否是红色加粗的”或者“进度条是否填充了50%”实现像素级的视觉回归测试。2.3 技术栈全景图为了达成跨平台的目标Midscene.js的架构设计得非常清晰统一核心层Midscene Core提供视觉驱动引擎的核心API如aiAct执行指令、aiQuery查询信息、aiAssert视觉断言。平台适配层针对不同平台封装了底层交互驱动。Web基于Playwright或Puppeteer用于控制浏览器和获取页面截图。Android基于ADBAndroid Debug Bridge和scrcpy或Appium用于控制设备和获取屏幕镜像。iOS基于WebDriverAgentWDA用于与iOS模拟器或真机交互。PC桌面通过截屏和模拟鼠标键盘事件如使用robotjs库来控制任意桌面应用。多模态模型层支持对接OpenAI GPT-4V、Google Gemini、以及开源的Qwen-VL、GLM-4V、UI-TARS等模型。你可以根据成本、网络环境、数据隐私要求灵活选择。实操心得一模型选择是成本与效果的平衡初期探索或对稳定性要求极高的场景可以优先使用GPT-4V或Gemini它们的UI理解能力目前是最顶尖的但API调用有成本和网络限制。对于需要大规模运行或数据敏感的内部系统自托管开源的Qwen-VL或GLM-4V是更优选择虽然需要一定的GPU资源但长期成本可控且完全私有化。3. 三步构建实战从零搭建跨平台AI测试理解了原理我们进入最核心的实战环节。我将以测试一个跨平台Web Android的TODO应用为例演示如何用三步构建自动化测试。3.1 第一步环境准备与初始化这一步的目标是搭建好Midscene.js的运行环境并准备好一个多模态模型。1. 创建项目并安装依赖# 初始化一个新的Node.js项目 mkdir midscene-demo cd midscene-demo npm init -y # 安装Midscene.js核心包及Web平台驱动Playwright npm install midscene/core midscene/web-playwright # 安装Playwright浏览器Midscene依赖它来驱动浏览器 npx playwright install chromium # 如果需要测试Android还需安装Android平台包 npm install midscene/android-adb # 并确保本地环境已配置好ADB和开启了开发者选项的Android设备/模拟器2. 配置模型访问Midscene.js需要通过API密钥访问多模态模型。我们以开源且效果不错的DeepSeek-VL为例假设我们使用其API服务。在项目根目录创建.env文件DEEPSEEK_API_KEYyour_deepseek_api_key_here MODEL_PROVIDERdeepseek MODEL_NAMEdeepseek-vl注意你需要去DeepSeek官网申请API Key。如果使用OpenAI则配置OPENAI_API_KEY和gpt-4-vision-preview。3. 编写基础配置脚本创建midscene.config.js这是Midscene的全局配置文件import { defineConfig } from midscene/core; export default defineConfig({ model: { provider: process.env.MODEL_PROVIDER || deepseek, name: process.env.MODEL_NAME || deepseek-vl, apiKey: process.env.DEEPSEEK_API_KEY, // 可配置模型参数如温度值影响输出的随机性 params: { temperature: 0.1, // 较低的温度使输出更确定适合自动化任务 } }, // 平台通用设置如超时时间 timeout: 120000, // 操作超时设为2分钟因为AI推理需要时间 // 截图和报告路径 screenshotDir: ./screenshots, reportDir: ./reports });3.2 第二步编写第一个视觉驱动测试用例我们以Web端的TODO应用为例编写一个测试打开应用添加一个待办项并验证它被成功添加。创建测试文件test/web-todo.spec.jsimport { test, expect } from playwright/test; // 可以使用Playwright的测试运行器 import { createWebAgent } from midscene/web-playwright; import config from ../midscene.config.js; test.describe(TODO App Web - AI视觉测试, () { let agent; test.beforeEach(async ({ page }) { // 初始化Midscene Agent绑定到Playwright的page对象 agent await createWebAgent(page, config); // 导航到待测应用这里用了一个在线示例 await page.goto(https://demo.playwright.dev/todomvc); // 等待页面基本加载可以加一个简单的AI指令让模型“感知”页面 await agent.aiAct(请查看当前页面是否加载完成主要内容是一个TODO输入框); }); test(应该能通过自然语言添加一个待办事项, async () { // 核心步骤1用自然语言告诉AI要做什么 await agent.aiAct(在页面的输入框中输入文本“购买周末食材”然后按下回车键添加); // 核心步骤2用AI查询当前页面状态验证结果 const todoItems await agent.aiQuery(liststring, 当前列表中所有待办项的文本内容); // 使用断言库进行验证 expect(todoItems).toContain(购买周末食材); // 可选核心步骤3进行视觉断言 const isItemVisible await agent.aiAssert(列表中应该有一条显示为“购买周末食材”的项目并且它未被勾选); expect(isItemVisible).toBe(true); }); test(应该能完成一个待办项, async () { // 先添加一个事项 await agent.aiAct(输入“阅读Midscene文档”并回车添加); // 使用AI找到并点击该事项左侧的完成勾选框 await agent.aiAct(找到文本为“阅读Midscene文档”的待办项点击它左边的圆形复选框来完成它); // 验证查询已完成项 const completedItems await agent.aiQuery(liststring, 所有已被标记为完成的待办项文本); expect(completedItems).toContain(阅读Midscene文档); // 验证视觉上该事项应有删除线样式这是传统选择器很难稳定断言的点 const isCompletedStyleCorrect await agent.aiAssert(文本“阅读Midscene文档”应该有一条横穿它的删除线); expect(isCompletedStyleCorrect).toBe(true); }); test.afterEach(async () { // 每个测试结束后可以生成一份可视化的执行报告 await agent.generateReport(./reports/${test.info().title}.html); }); });代码解析与技巧aiAct: 最常用的方法用于执行一个动作指令。AI会规划如何完成这个指令如先定位输入框再输入再找回车键。aiQuery: 用于向AI提问获取页面上的信息。返回类型需要在指令中声明如liststringAI会尝试解析并返回结构化的数据。aiAssert: 用于进行视觉断言。它返回一个布尔值表示当前屏幕是否满足描述的状态。指令的精确性指令越清晰AI执行越准确。“点击登录按钮”比“点击按钮”好。“在顶部的导航栏找到并点击登录按钮”则更好。实操心得二指令工程Prompt Engineering是关键初期使用可能会觉得AI“不听指挥”。问题往往出在指令上。我的经验是“角色上下文清晰动作预期反馈”。例如“你现在是一个测试员正在操作一个TODO应用。请找到页面中唯一的文本输入框将光标聚焦其中输入‘测试AI自动化’这六个汉字然后找到键盘上的回车键图标或区域并点击。请确保输入框内的文字已变为‘测试AI自动化’后再执行回车。” 这样的指令成功率远高于简单的“输入测试AI自动化并回车”。3.3 第三步扩展到Android平台与复杂场景Midscene的强大在于跨平台API的一致性。将Web测试迁移到Android原生APP测试代码结构几乎不变。1. Android环境准备确保有一台已开启USB调试的Android设备或模拟器。电脑上安装好ADB并确保设备连接adb devices能看到设备。2. 编写Android测试脚本创建test/android-todo.spec.jsimport { createAndroidAgent } from midscene/android-adb; import config from ../midscene.config.js; describe(TODO App Android - AI视觉测试, () { let agent; beforeAll(async () { // 初始化Android Agent传入设备序列号默认取第一个 agent await createAndroidAgent({ config }); // 启动待测APP以Google Keep为例 await agent.aiAct(在设备主屏幕找到并打开名为“Keep”的绿色图标应用); }); test(在Keep中创建并归档一个笔记, async () { // 步骤1创建笔记 await agent.aiAct(在应用底部找到那个大大的“”号浮动按钮点击它); await agent.aiAct(在出现的标题输入框中输入“Midscene测试笔记”); await agent.aiAct(在下方的正文区域输入“这是一个由AI视觉驱动自动创建的测试内容。”); await agent.aiAct(点击屏幕右上角的“完成”或“保存”按钮通常是一个对勾图标); // 步骤2验证笔记创建成功 const noteTitles await agent.aiQuery(liststring, 当前笔记列表页面中所有笔记卡片的标题文本); expect(noteTitles).toContain(Midscene测试笔记); // 步骤3归档笔记一个更复杂的操作链 await agent.aiAct(长按标题为“Midscene测试笔记”的卡片直到它被选中通常会出现勾选框或背景变色); await agent.aiAct(现在屏幕顶部或底部应该出现了操作工具栏找到并点击看起来像“归档”或一个文件夹向下箭头的图标); await agent.aiAct(如果有确认对话框弹出点击“确认”或“归档”按钮); // 步骤4切换到归档视图验证 await agent.aiAct(点击屏幕左上角的三条横线菜单图标); await agent.aiAct(在滑出的侧边栏菜单中找到并点击“归档”或“已归档”选项); const archivedNoteTitles await agent.aiQuery(liststring, 当前归档页面中所有笔记的标题); expect(archivedNoteTitles).toContain(Midscene测试笔记); }); afterAll(async () { // 测试结束关闭应用清理资源 await agent.aiAct(返回设备主屏幕); await agent.close(); }); });跨平台协同测试示例假设我们有一个Web管理后台和一个Android员工端应用需要测试一个“创建任务-移动端接收”的流程// test/cross-platform-workflow.spec.js import { createWebAgent } from midscene/web-playwright; import { createAndroidAgent } from midscene/android-adb; test(Web创建任务Android端同步显示, async ({ page }) { // 1. Web后台创建任务 const webAgent await createWebAgent(page, webConfig); await page.goto(https://admin.company.com); await webAgent.aiAct(登录系统账号admin密码123456); await webAgent.aiAct(在侧边栏点击“任务管理”然后点击“新建任务”按钮); await webAgent.aiAct(在表单中标题输入“紧急服务器巡检”负责人选择“张三”点击提交); // 获取创建的任务ID假设AI可以从成功提示中提取 const taskId await webAgent.aiQuery(string, 刚刚创建成功的任务它的编号ID是什么); // 2. Android端检查任务 const androidAgent await createAndroidAgent({ config: androidConfig }); await androidAgent.aiAct(解锁手机打开“企业办公”APP使用张三的账号登录); await androidAgent.aiAct(点击底部导航栏的“待办任务”选项卡); // 使用Web端获取的ID进行验证 const isTaskPresent await androidAgent.aiAssert(在任务列表中应该存在一条包含编号“${taskId}”和标题“服务器巡检”的任务卡片); expect(isTaskPresent).toBe(true); });4. 高级策略与性能优化当测试用例多起来后我们需要考虑稳定性、速度和成本。4.1 两种自动化风格的选择Midscene支持两种风格对应不同的场景风格实现方式优点缺点适用场景自动规划使用aiAct描述一个完整目标让AI自主分解步骤。代码极其简洁像写测试用例文档。易于快速原型验证。执行路径不可控可能因AI“思维”不同而产生波动。调试困难。成本较高每次都要调用模型规划。探索性测试、一次性任务、流程相对固定且简单的场景。工作流风格开发者用aiQuery、aiTap等细粒度API控制流程AI只负责定位和简单判断。流程控制精准稳定性高。可复用逻辑组件。长期运行成本更低规划次数少。代码量相对较多需要更多开发工作。核心业务流程测试、回归测试套件、对稳定性和成本有要求的量产环境。工作流风格代码示例更稳定// 自动规划风格脆弱 await agent.aiAct(登录系统然后找到昨天的销售报表导出为Excel最后退出登录); // 工作流风格稳定可控 await agent.aiTap(登录按钮); await agent.aiFill(用户名输入框, zhangsan); await agent.aiFill(密码输入框, pass123); await agent.aiTap(提交按钮); // 使用aiQuery获取结构化数据再用代码逻辑处理 const menuItems await agent.aiQuery(liststring, 主导航栏中所有一级菜单的文本); const reportMenuIndex menuItems.findIndex(item item.includes(报表)); await agent.aiTap(主导航栏第${reportMenuIndex 1}个项目); const subItems await agent.aiQuery(liststring, 当前展开的子菜单中所有选项文本); const targetReport subItems.find(item item.includes(销售) item.includes(昨日)); await agent.aiTap(targetReport); await agent.aiTap(页面上看起来像“导出”或“下载”的按钮); const exportOptions await agent.aiQuery(liststring, 弹出菜单或模态框中的所有选项文本); const excelOption exportOptions.find(opt opt.toLowerCase().includes(excel)); await agent.aiTap(excelOption); await agent.aiTap(用户头像或姓名区域); await agent.aiTap(退出登录或注销按钮);4.2 模型策略与缓存优化模型API调用是主要的时间消耗和成本来源。Midscene提供了缓存机制来大幅优化。1. 启用规划缓存Planning CacheAI对同一指令在同一界面下的规划结果是相似的。我们可以缓存起来。// 在midscene.config.js中配置 export default defineConfig({ model: { /* ... */ }, cache: { enabled: true, // 缓存存储路径 dir: ./.midscene_cache, // 缓存策略strict (界面截图哈希严格匹配) 或 loose (相似即可) strategy: loose, }, });启用后首次执行aiAct(点击登录按钮)时Midscene会计算当前屏幕的哈希值并将模型返回的“点击坐标”存入缓存。下次在视觉上高度相似的界面执行相同指令时会直接使用缓存的结果跳过模型调用速度从秒级提升到毫秒级。2. 多模型降级策略可以在配置中定义模型优先级列表当主模型不可用或失败时自动降级。export default defineConfig({ model: [ { provider: openai, name: gpt-4-vision-preview, apiKey: process.env.OPENAI_KEY, priority: 1, // 最高优先级 }, { provider: deepseek, name: deepseek-vl, apiKey: process.env.DEEPSEEK_KEY, priority: 2, // 备用 }, { provider: local, // 本地部署的模型 name: qwen-vl, endpoint: http://localhost:8080/v1, priority: 3, } ], });4.3 集成到现有CI/CD流水线Midscene测试可以无缝集成到Jenkins、GitHub Actions等CI/CD工具中。GitHub Actions示例.github/workflows/ai-visual-test.ymlname: AI Visual Tests on: [push, pull_request] jobs: test-web: runs-on: ubuntu-latest steps: - uses: actions/checkoutv3 - uses: actions/setup-nodev3 with: { node-version: 18 } - run: npm ci - run: npx playwright install --with-deps chromium - run: npm run test:web env: DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }} - uses: actions/upload-artifactv3 if: always() # 无论测试成功与否都上传报告 with: name: ai-test-reports-web path: reports/ test-android: runs-on: macos-latest # Android模拟器需要macOS或Linux steps: - uses: actions/checkoutv3 - uses: actions/setup-nodev3 - run: npm ci - name: Setup Android Emulator uses: reactivecircus/android-emulator-runnerv2 with: api-level: 33 target: google_apis arch: x86_64 profile: Nexus 6 script: echo Emulator booted - run: npm run test:android env: DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }} - uses: actions/upload-artifactv3 if: always() with: name: ai-test-reports-android path: reports/5. 常见问题与排查技巧实录在实际使用中你肯定会遇到各种问题。下面是我踩过坑后总结的“避坑指南”。5.1 AI执行失败或不准这是最常见的问题通常不是工具bug而是指令或环境问题。问题1AI找不到元素或点击位置偏移可能原因1指令模糊。“点击按钮”在满屏都是按钮的页面里无效。解决提供更丰富的上下文。如“在页面顶部的导航栏区域找到一个背景蓝色、文字白色的‘提交’按钮”。可能原因2屏幕分辨率/缩放导致坐标计算错误。解决确保测试环境浏览器窗口大小、设备分辨率固定。在beforeEach中统一设置窗口尺寸await page.setViewportSize({ width: 1920, height: 1080 })。可能原因3动态内容未加载完成。解决在关键操作前增加显式等待。Midscene的aiAct内部有重试机制但你也可以结合Playwright的等待await page.waitForLoadState(networkidle);然后再执行AI指令。问题2aiQuery返回的数据格式不符合预期可能原因指令中声明的返回类型与AI理解不符。解决类型声明要非常精确。想要数组就写liststring想要布尔值就写boolean。示例const price await agent.aiQuery(number, 商品详情页中当前商品的价格只返回数字不要货币符号);问题3运行速度慢可能原因每次操作都调用模型网络延迟高。解决务必开启缓存这是性能提升的关键。使用工作流风格减少aiAct的使用多用aiTap、aiFill等低级API这些API在缓存命中时极快。考虑使用更快的模型如gemini-1.5-flash或本地部署的轻量级模型。5.2 环境与配置问题问题Android测试连接失败排查adb devices确认设备已连接并授权。检查Midscene Android配置中是否指定了正确的设备序列号多设备时。确保待测APP的包名和Activity名正确如果使用launchApp方式。问题模型API调用返回授权错误排查检查.env文件中的API_KEY是否正确是否有空格。检查模型提供商账户的余额或配额是否充足。如果是本地部署模型检查服务地址端口是否正确模型是否加载成功。5.3 可视化报告解读Midscene生成的HTML报告是强大的调试工具。报告里不仅包含每一步的截图更重要的是展示了AI的“思考过程”。规划步骤Planning Steps展示了AI将你的自然语言指令分解成了哪些子步骤。定位高亮Located Element用红色框标出了AI决定要操作或查看的屏幕区域。操作结果Action Result显示操作是否成功以及执行后的屏幕状态。当测试失败时第一件事就是打开报告看AI在哪一步的“理解”或“定位”上出了偏差然后有针对性地优化你的指令或前置条件。6. 总结与展望走完这三步你应该已经成功搭建起一个跨平台的、以视觉为核心的AI自动化测试原型。回顾一下它的核心价值在于将测试脚本的维护焦点从易变的底层实现细节转移到了相对稳定的视觉表现层。这带来的不仅仅是脚本稳定性的提升更是一种测试范式的转变——测试用例变得更像一份人类可读的验收文档。当然这项技术并非银弹。它目前仍有局限性对复杂动态界面如频繁刷新的数据列表的定位可能不稳定模型API调用存在成本和延迟极端复杂的逻辑判断仍需结合传统代码。因此我的建议是采用混合策略Hybrid Approach在传统选择器稳定且高效的地方如固定的导航栏、表单输入框继续使用Playwright/Appium在那些“选择器地狱”、Canvas渲染、或者需要视觉验证的场景引入Midscene.js作为强有力的补充。从我个人的实践来看Midscene.js特别适合以下几类场景跨平台核心业务流程的冒烟测试。对UI视觉还原度要求极高的验收测试。快速为没有测试基础的老项目搭建自动化用例。自动化那些操作路径固定但界面元素难以用传统方式定位的重复性任务。技术总是在解决旧问题的同时带来新的挑战和可能性。视觉驱动自动化无疑为我们打开了一扇新的大门。它或许还不能完全替代传统自动化但它正在成为一个不可或缺的强力补充。最重要的是它让我们从无穷无尽的选择器维护中解放出来能够更专注于测试本身的价值——保障产品质量与用户体验。
视觉驱动UI自动化:Midscene.js三步构建跨平台AI测试方案
1. 项目概述当UI自动化不再依赖DOM大家好我是老张一个在测试开发领域摸爬滚打了十多年的老兵。这些年从Selenium到Playwright从Appium到Cypress我几乎把市面上主流的UI自动化框架都用了个遍。但有一个痛点始终如影随形页面结构DOM/View Hierarchy的脆弱性。产品经理一个心血来潮的改版开发同学一次看似无害的重构就能让精心维护的自动化脚本瞬间“瘫痪”测试同学就得连夜加班去更新那些脆弱的选择器XPath、CSS Selector。更别提那些纯图标按钮、Canvas渲染的游戏界面、或者跨域的iframe对于传统基于结构的自动化工具来说简直就是“盲区”。最近一个名为Midscene.js的开源项目进入了我的视野它提出了一种截然不同的思路视觉驱动Vision-Driven。简单说它不关心你的页面底层是什么结构它只“看”屏幕截图然后像真人一样根据你描述的自然语言去理解和操作界面。这个概念让我眼前一亮因为它直击了传统UI自动化的命门。今天我就结合自己的实操经验和大家详细拆解如何用Midscene.js仅需三步构建一套真正健壮、跨平台的AI自动化测试方案。无论你是测试工程师、前端开发者还是对RPA机器人流程自动化感兴趣的朋友这套方案都可能为你打开一扇新的大门。2. 核心思路拆解为什么是“视觉驱动”在深入三步构建法之前我们必须先理解Midscene.js赖以生存的“视觉驱动”理念以及它为何能解决我们多年的痛点。2.1 传统结构依赖的“阿喀琉斯之踵”传统的UI自动化无论是Web的Playwright还是移动端的Appium其核心工作原理都是通过浏览器开发者工具或移动端无障碍服务获取应用界面的结构化描述DOM树、View Hierarchy。自动化脚本通过编写选择器在这个结构树中定位元素然后执行点击、输入等操作。这套模式的弊端非常明显极度脆弱前端框架升级、UI组件库更换、甚至只是开发修改了一个div的class名都可能导致选择器失效。测试脚本的维护成本Test Maintenance Cost常年居高不下。覆盖不全对于非标准控件、完全由Canvas/WebGL渲染的内容如图表、游戏、缺乏无障碍标签的纯图标按钮结构树中要么没有对应节点要么节点信息缺失自动化工具“看不见”它们。跨域隔离Web中的跨域iframe其内部的DOM对于外层页面的脚本是不可直接访问的这给自动化带来了巨大障碍。验证片面传统断言Assertion通常只验证某个元素是否存在、属性值是否正确但无法验证这个元素在用户眼中实际呈现的效果是否正确比如颜色偏差、布局错乱、图像加载失败等视觉问题。2.2 Midscene.js的破局之道像人一样“看”和“做”Midscene.js绕开了结构树选择了一条更接近人类本能的方式输入当前屏幕的截图 一句自然语言指令如“点击登录按钮”。核心调用一个强大的多模态大模型Multimodal LLM例如Qwen-VL、GLM-4V等。这些模型经过海量图像和文本的联合训练具备惊人的视觉理解和推理能力。过程模型“看”着截图理解你的指令在图像中定位目标如找到“登录按钮”的位置并规划出操作如计算点击的坐标。输出Midscene.js接收模型返回的坐标和操作类型通过底层驱动如Playwright、ADB执行真实的点击操作。这个过程带来了几个革命性的优势抗变化能力强只要“登录按钮”在屏幕上看起来还是个按钮并且人能认出来无论它的底层HTML标签从button变成了div还是class名从btn-primary改成了submit-btnMidscene.js都能找到并点击它。脚本的稳定性大幅提升。无所不“见”只要是屏幕上像素能呈现的内容无论是Canvas图表、游戏UI、桌面应用窗口还是移动端原生控件模型都能尝试理解。真正实现了“所见即可测”。真正的视觉验证你可以直接让AI判断“这个弹窗的标题是否是红色加粗的”或者“进度条是否填充了50%”实现像素级的视觉回归测试。2.3 技术栈全景图为了达成跨平台的目标Midscene.js的架构设计得非常清晰统一核心层Midscene Core提供视觉驱动引擎的核心API如aiAct执行指令、aiQuery查询信息、aiAssert视觉断言。平台适配层针对不同平台封装了底层交互驱动。Web基于Playwright或Puppeteer用于控制浏览器和获取页面截图。Android基于ADBAndroid Debug Bridge和scrcpy或Appium用于控制设备和获取屏幕镜像。iOS基于WebDriverAgentWDA用于与iOS模拟器或真机交互。PC桌面通过截屏和模拟鼠标键盘事件如使用robotjs库来控制任意桌面应用。多模态模型层支持对接OpenAI GPT-4V、Google Gemini、以及开源的Qwen-VL、GLM-4V、UI-TARS等模型。你可以根据成本、网络环境、数据隐私要求灵活选择。实操心得一模型选择是成本与效果的平衡初期探索或对稳定性要求极高的场景可以优先使用GPT-4V或Gemini它们的UI理解能力目前是最顶尖的但API调用有成本和网络限制。对于需要大规模运行或数据敏感的内部系统自托管开源的Qwen-VL或GLM-4V是更优选择虽然需要一定的GPU资源但长期成本可控且完全私有化。3. 三步构建实战从零搭建跨平台AI测试理解了原理我们进入最核心的实战环节。我将以测试一个跨平台Web Android的TODO应用为例演示如何用三步构建自动化测试。3.1 第一步环境准备与初始化这一步的目标是搭建好Midscene.js的运行环境并准备好一个多模态模型。1. 创建项目并安装依赖# 初始化一个新的Node.js项目 mkdir midscene-demo cd midscene-demo npm init -y # 安装Midscene.js核心包及Web平台驱动Playwright npm install midscene/core midscene/web-playwright # 安装Playwright浏览器Midscene依赖它来驱动浏览器 npx playwright install chromium # 如果需要测试Android还需安装Android平台包 npm install midscene/android-adb # 并确保本地环境已配置好ADB和开启了开发者选项的Android设备/模拟器2. 配置模型访问Midscene.js需要通过API密钥访问多模态模型。我们以开源且效果不错的DeepSeek-VL为例假设我们使用其API服务。在项目根目录创建.env文件DEEPSEEK_API_KEYyour_deepseek_api_key_here MODEL_PROVIDERdeepseek MODEL_NAMEdeepseek-vl注意你需要去DeepSeek官网申请API Key。如果使用OpenAI则配置OPENAI_API_KEY和gpt-4-vision-preview。3. 编写基础配置脚本创建midscene.config.js这是Midscene的全局配置文件import { defineConfig } from midscene/core; export default defineConfig({ model: { provider: process.env.MODEL_PROVIDER || deepseek, name: process.env.MODEL_NAME || deepseek-vl, apiKey: process.env.DEEPSEEK_API_KEY, // 可配置模型参数如温度值影响输出的随机性 params: { temperature: 0.1, // 较低的温度使输出更确定适合自动化任务 } }, // 平台通用设置如超时时间 timeout: 120000, // 操作超时设为2分钟因为AI推理需要时间 // 截图和报告路径 screenshotDir: ./screenshots, reportDir: ./reports });3.2 第二步编写第一个视觉驱动测试用例我们以Web端的TODO应用为例编写一个测试打开应用添加一个待办项并验证它被成功添加。创建测试文件test/web-todo.spec.jsimport { test, expect } from playwright/test; // 可以使用Playwright的测试运行器 import { createWebAgent } from midscene/web-playwright; import config from ../midscene.config.js; test.describe(TODO App Web - AI视觉测试, () { let agent; test.beforeEach(async ({ page }) { // 初始化Midscene Agent绑定到Playwright的page对象 agent await createWebAgent(page, config); // 导航到待测应用这里用了一个在线示例 await page.goto(https://demo.playwright.dev/todomvc); // 等待页面基本加载可以加一个简单的AI指令让模型“感知”页面 await agent.aiAct(请查看当前页面是否加载完成主要内容是一个TODO输入框); }); test(应该能通过自然语言添加一个待办事项, async () { // 核心步骤1用自然语言告诉AI要做什么 await agent.aiAct(在页面的输入框中输入文本“购买周末食材”然后按下回车键添加); // 核心步骤2用AI查询当前页面状态验证结果 const todoItems await agent.aiQuery(liststring, 当前列表中所有待办项的文本内容); // 使用断言库进行验证 expect(todoItems).toContain(购买周末食材); // 可选核心步骤3进行视觉断言 const isItemVisible await agent.aiAssert(列表中应该有一条显示为“购买周末食材”的项目并且它未被勾选); expect(isItemVisible).toBe(true); }); test(应该能完成一个待办项, async () { // 先添加一个事项 await agent.aiAct(输入“阅读Midscene文档”并回车添加); // 使用AI找到并点击该事项左侧的完成勾选框 await agent.aiAct(找到文本为“阅读Midscene文档”的待办项点击它左边的圆形复选框来完成它); // 验证查询已完成项 const completedItems await agent.aiQuery(liststring, 所有已被标记为完成的待办项文本); expect(completedItems).toContain(阅读Midscene文档); // 验证视觉上该事项应有删除线样式这是传统选择器很难稳定断言的点 const isCompletedStyleCorrect await agent.aiAssert(文本“阅读Midscene文档”应该有一条横穿它的删除线); expect(isCompletedStyleCorrect).toBe(true); }); test.afterEach(async () { // 每个测试结束后可以生成一份可视化的执行报告 await agent.generateReport(./reports/${test.info().title}.html); }); });代码解析与技巧aiAct: 最常用的方法用于执行一个动作指令。AI会规划如何完成这个指令如先定位输入框再输入再找回车键。aiQuery: 用于向AI提问获取页面上的信息。返回类型需要在指令中声明如liststringAI会尝试解析并返回结构化的数据。aiAssert: 用于进行视觉断言。它返回一个布尔值表示当前屏幕是否满足描述的状态。指令的精确性指令越清晰AI执行越准确。“点击登录按钮”比“点击按钮”好。“在顶部的导航栏找到并点击登录按钮”则更好。实操心得二指令工程Prompt Engineering是关键初期使用可能会觉得AI“不听指挥”。问题往往出在指令上。我的经验是“角色上下文清晰动作预期反馈”。例如“你现在是一个测试员正在操作一个TODO应用。请找到页面中唯一的文本输入框将光标聚焦其中输入‘测试AI自动化’这六个汉字然后找到键盘上的回车键图标或区域并点击。请确保输入框内的文字已变为‘测试AI自动化’后再执行回车。” 这样的指令成功率远高于简单的“输入测试AI自动化并回车”。3.3 第三步扩展到Android平台与复杂场景Midscene的强大在于跨平台API的一致性。将Web测试迁移到Android原生APP测试代码结构几乎不变。1. Android环境准备确保有一台已开启USB调试的Android设备或模拟器。电脑上安装好ADB并确保设备连接adb devices能看到设备。2. 编写Android测试脚本创建test/android-todo.spec.jsimport { createAndroidAgent } from midscene/android-adb; import config from ../midscene.config.js; describe(TODO App Android - AI视觉测试, () { let agent; beforeAll(async () { // 初始化Android Agent传入设备序列号默认取第一个 agent await createAndroidAgent({ config }); // 启动待测APP以Google Keep为例 await agent.aiAct(在设备主屏幕找到并打开名为“Keep”的绿色图标应用); }); test(在Keep中创建并归档一个笔记, async () { // 步骤1创建笔记 await agent.aiAct(在应用底部找到那个大大的“”号浮动按钮点击它); await agent.aiAct(在出现的标题输入框中输入“Midscene测试笔记”); await agent.aiAct(在下方的正文区域输入“这是一个由AI视觉驱动自动创建的测试内容。”); await agent.aiAct(点击屏幕右上角的“完成”或“保存”按钮通常是一个对勾图标); // 步骤2验证笔记创建成功 const noteTitles await agent.aiQuery(liststring, 当前笔记列表页面中所有笔记卡片的标题文本); expect(noteTitles).toContain(Midscene测试笔记); // 步骤3归档笔记一个更复杂的操作链 await agent.aiAct(长按标题为“Midscene测试笔记”的卡片直到它被选中通常会出现勾选框或背景变色); await agent.aiAct(现在屏幕顶部或底部应该出现了操作工具栏找到并点击看起来像“归档”或一个文件夹向下箭头的图标); await agent.aiAct(如果有确认对话框弹出点击“确认”或“归档”按钮); // 步骤4切换到归档视图验证 await agent.aiAct(点击屏幕左上角的三条横线菜单图标); await agent.aiAct(在滑出的侧边栏菜单中找到并点击“归档”或“已归档”选项); const archivedNoteTitles await agent.aiQuery(liststring, 当前归档页面中所有笔记的标题); expect(archivedNoteTitles).toContain(Midscene测试笔记); }); afterAll(async () { // 测试结束关闭应用清理资源 await agent.aiAct(返回设备主屏幕); await agent.close(); }); });跨平台协同测试示例假设我们有一个Web管理后台和一个Android员工端应用需要测试一个“创建任务-移动端接收”的流程// test/cross-platform-workflow.spec.js import { createWebAgent } from midscene/web-playwright; import { createAndroidAgent } from midscene/android-adb; test(Web创建任务Android端同步显示, async ({ page }) { // 1. Web后台创建任务 const webAgent await createWebAgent(page, webConfig); await page.goto(https://admin.company.com); await webAgent.aiAct(登录系统账号admin密码123456); await webAgent.aiAct(在侧边栏点击“任务管理”然后点击“新建任务”按钮); await webAgent.aiAct(在表单中标题输入“紧急服务器巡检”负责人选择“张三”点击提交); // 获取创建的任务ID假设AI可以从成功提示中提取 const taskId await webAgent.aiQuery(string, 刚刚创建成功的任务它的编号ID是什么); // 2. Android端检查任务 const androidAgent await createAndroidAgent({ config: androidConfig }); await androidAgent.aiAct(解锁手机打开“企业办公”APP使用张三的账号登录); await androidAgent.aiAct(点击底部导航栏的“待办任务”选项卡); // 使用Web端获取的ID进行验证 const isTaskPresent await androidAgent.aiAssert(在任务列表中应该存在一条包含编号“${taskId}”和标题“服务器巡检”的任务卡片); expect(isTaskPresent).toBe(true); });4. 高级策略与性能优化当测试用例多起来后我们需要考虑稳定性、速度和成本。4.1 两种自动化风格的选择Midscene支持两种风格对应不同的场景风格实现方式优点缺点适用场景自动规划使用aiAct描述一个完整目标让AI自主分解步骤。代码极其简洁像写测试用例文档。易于快速原型验证。执行路径不可控可能因AI“思维”不同而产生波动。调试困难。成本较高每次都要调用模型规划。探索性测试、一次性任务、流程相对固定且简单的场景。工作流风格开发者用aiQuery、aiTap等细粒度API控制流程AI只负责定位和简单判断。流程控制精准稳定性高。可复用逻辑组件。长期运行成本更低规划次数少。代码量相对较多需要更多开发工作。核心业务流程测试、回归测试套件、对稳定性和成本有要求的量产环境。工作流风格代码示例更稳定// 自动规划风格脆弱 await agent.aiAct(登录系统然后找到昨天的销售报表导出为Excel最后退出登录); // 工作流风格稳定可控 await agent.aiTap(登录按钮); await agent.aiFill(用户名输入框, zhangsan); await agent.aiFill(密码输入框, pass123); await agent.aiTap(提交按钮); // 使用aiQuery获取结构化数据再用代码逻辑处理 const menuItems await agent.aiQuery(liststring, 主导航栏中所有一级菜单的文本); const reportMenuIndex menuItems.findIndex(item item.includes(报表)); await agent.aiTap(主导航栏第${reportMenuIndex 1}个项目); const subItems await agent.aiQuery(liststring, 当前展开的子菜单中所有选项文本); const targetReport subItems.find(item item.includes(销售) item.includes(昨日)); await agent.aiTap(targetReport); await agent.aiTap(页面上看起来像“导出”或“下载”的按钮); const exportOptions await agent.aiQuery(liststring, 弹出菜单或模态框中的所有选项文本); const excelOption exportOptions.find(opt opt.toLowerCase().includes(excel)); await agent.aiTap(excelOption); await agent.aiTap(用户头像或姓名区域); await agent.aiTap(退出登录或注销按钮);4.2 模型策略与缓存优化模型API调用是主要的时间消耗和成本来源。Midscene提供了缓存机制来大幅优化。1. 启用规划缓存Planning CacheAI对同一指令在同一界面下的规划结果是相似的。我们可以缓存起来。// 在midscene.config.js中配置 export default defineConfig({ model: { /* ... */ }, cache: { enabled: true, // 缓存存储路径 dir: ./.midscene_cache, // 缓存策略strict (界面截图哈希严格匹配) 或 loose (相似即可) strategy: loose, }, });启用后首次执行aiAct(点击登录按钮)时Midscene会计算当前屏幕的哈希值并将模型返回的“点击坐标”存入缓存。下次在视觉上高度相似的界面执行相同指令时会直接使用缓存的结果跳过模型调用速度从秒级提升到毫秒级。2. 多模型降级策略可以在配置中定义模型优先级列表当主模型不可用或失败时自动降级。export default defineConfig({ model: [ { provider: openai, name: gpt-4-vision-preview, apiKey: process.env.OPENAI_KEY, priority: 1, // 最高优先级 }, { provider: deepseek, name: deepseek-vl, apiKey: process.env.DEEPSEEK_KEY, priority: 2, // 备用 }, { provider: local, // 本地部署的模型 name: qwen-vl, endpoint: http://localhost:8080/v1, priority: 3, } ], });4.3 集成到现有CI/CD流水线Midscene测试可以无缝集成到Jenkins、GitHub Actions等CI/CD工具中。GitHub Actions示例.github/workflows/ai-visual-test.ymlname: AI Visual Tests on: [push, pull_request] jobs: test-web: runs-on: ubuntu-latest steps: - uses: actions/checkoutv3 - uses: actions/setup-nodev3 with: { node-version: 18 } - run: npm ci - run: npx playwright install --with-deps chromium - run: npm run test:web env: DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }} - uses: actions/upload-artifactv3 if: always() # 无论测试成功与否都上传报告 with: name: ai-test-reports-web path: reports/ test-android: runs-on: macos-latest # Android模拟器需要macOS或Linux steps: - uses: actions/checkoutv3 - uses: actions/setup-nodev3 - run: npm ci - name: Setup Android Emulator uses: reactivecircus/android-emulator-runnerv2 with: api-level: 33 target: google_apis arch: x86_64 profile: Nexus 6 script: echo Emulator booted - run: npm run test:android env: DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }} - uses: actions/upload-artifactv3 if: always() with: name: ai-test-reports-android path: reports/5. 常见问题与排查技巧实录在实际使用中你肯定会遇到各种问题。下面是我踩过坑后总结的“避坑指南”。5.1 AI执行失败或不准这是最常见的问题通常不是工具bug而是指令或环境问题。问题1AI找不到元素或点击位置偏移可能原因1指令模糊。“点击按钮”在满屏都是按钮的页面里无效。解决提供更丰富的上下文。如“在页面顶部的导航栏区域找到一个背景蓝色、文字白色的‘提交’按钮”。可能原因2屏幕分辨率/缩放导致坐标计算错误。解决确保测试环境浏览器窗口大小、设备分辨率固定。在beforeEach中统一设置窗口尺寸await page.setViewportSize({ width: 1920, height: 1080 })。可能原因3动态内容未加载完成。解决在关键操作前增加显式等待。Midscene的aiAct内部有重试机制但你也可以结合Playwright的等待await page.waitForLoadState(networkidle);然后再执行AI指令。问题2aiQuery返回的数据格式不符合预期可能原因指令中声明的返回类型与AI理解不符。解决类型声明要非常精确。想要数组就写liststring想要布尔值就写boolean。示例const price await agent.aiQuery(number, 商品详情页中当前商品的价格只返回数字不要货币符号);问题3运行速度慢可能原因每次操作都调用模型网络延迟高。解决务必开启缓存这是性能提升的关键。使用工作流风格减少aiAct的使用多用aiTap、aiFill等低级API这些API在缓存命中时极快。考虑使用更快的模型如gemini-1.5-flash或本地部署的轻量级模型。5.2 环境与配置问题问题Android测试连接失败排查adb devices确认设备已连接并授权。检查Midscene Android配置中是否指定了正确的设备序列号多设备时。确保待测APP的包名和Activity名正确如果使用launchApp方式。问题模型API调用返回授权错误排查检查.env文件中的API_KEY是否正确是否有空格。检查模型提供商账户的余额或配额是否充足。如果是本地部署模型检查服务地址端口是否正确模型是否加载成功。5.3 可视化报告解读Midscene生成的HTML报告是强大的调试工具。报告里不仅包含每一步的截图更重要的是展示了AI的“思考过程”。规划步骤Planning Steps展示了AI将你的自然语言指令分解成了哪些子步骤。定位高亮Located Element用红色框标出了AI决定要操作或查看的屏幕区域。操作结果Action Result显示操作是否成功以及执行后的屏幕状态。当测试失败时第一件事就是打开报告看AI在哪一步的“理解”或“定位”上出了偏差然后有针对性地优化你的指令或前置条件。6. 总结与展望走完这三步你应该已经成功搭建起一个跨平台的、以视觉为核心的AI自动化测试原型。回顾一下它的核心价值在于将测试脚本的维护焦点从易变的底层实现细节转移到了相对稳定的视觉表现层。这带来的不仅仅是脚本稳定性的提升更是一种测试范式的转变——测试用例变得更像一份人类可读的验收文档。当然这项技术并非银弹。它目前仍有局限性对复杂动态界面如频繁刷新的数据列表的定位可能不稳定模型API调用存在成本和延迟极端复杂的逻辑判断仍需结合传统代码。因此我的建议是采用混合策略Hybrid Approach在传统选择器稳定且高效的地方如固定的导航栏、表单输入框继续使用Playwright/Appium在那些“选择器地狱”、Canvas渲染、或者需要视觉验证的场景引入Midscene.js作为强有力的补充。从我个人的实践来看Midscene.js特别适合以下几类场景跨平台核心业务流程的冒烟测试。对UI视觉还原度要求极高的验收测试。快速为没有测试基础的老项目搭建自动化用例。自动化那些操作路径固定但界面元素难以用传统方式定位的重复性任务。技术总是在解决旧问题的同时带来新的挑战和可能性。视觉驱动自动化无疑为我们打开了一扇新的大门。它或许还不能完全替代传统自动化但它正在成为一个不可或缺的强力补充。最重要的是它让我们从无穷无尽的选择器维护中解放出来能够更专注于测试本身的价值——保障产品质量与用户体验。