news 2026/7/7 19:47:21

Midscene.js:视觉驱动自动化测试,突破传统UI测试的局限

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Midscene.js:视觉驱动自动化测试,突破传统UI测试的局限

1. 项目概述:当UI测试遇上“视觉智能”

在UI自动化测试这个老生常谈的领域里,我们似乎已经习惯了与各种选择器(Selector)的缠斗。无论是CSS选择器、XPath,还是各种框架提供的ID、ClassName,维护这些脆弱的“钩子”已经成为测试工程师的日常。一个看似微小的UI改动——比如一个按钮从<button id="submit">变成了<button>mkdir midscene-demo && cd midscene-demo npm init -y

安装必要的依赖。这里我们以Playwright作为浏览器驱动。

npm install @midscene/sdk playwright # 安装Playwright的浏览器内核 npx playwright install chromium

接下来,你需要配置Midscene.js的API密钥。Midscene.js的部分模型服务可能需要认证(特别是使用其托管的高级模型时)。前往Midscene官网注册并获取API Key,然后在项目根目录创建.env文件:

MIDSCENE_API_KEY=your_api_key_here

4.2 编写第一个视觉驱动测试脚本

我们创建一个测试文件test/add-to-cart.spec.js。假设我们要测试的流程是:打开电商网站,搜索“无线耳机”,在结果列表中找到第一个商品,点击进入详情页,然后点击“加入购物车”按钮,最后验证购物车图标上的数量是否变为1。

const { midscene } = require('@midscene/sdk'); const { chromium } = require('playwright'); require('dotenv').config(); // 加载环境变量 (async () => { // 1. 启动浏览器和页面 const browser = await chromium.launch({ headless: false }); // 非无头模式,方便观察 const context = await browser.newContext(); const page = await context.newPage(); // 2. 初始化Midscene,并绑定到当前页面 const ms = await midscene.init({ page: page, // 传入Playwright page对象 apiKey: process.env.MIDSCENE_API_KEY, model: 'seed', // 使用豆包Seed模型 }); try { // 3. 导航到目标网站 await page.goto('https://demo-ecommerce.example.com'); // 4. 使用自然语言进行搜索 await ms.aiAct('在顶部的搜索框中输入“无线耳机”,然后按下回车键进行搜索'); // Midscene.js会视觉定位到搜索框,输入文本,并找到回车键或搜索按钮执行 // 5. 等待搜索结果加载,并点击第一个商品 await page.waitForTimeout(2000); // 简单等待,生产中应用更可靠的等待机制 await ms.aiTap('商品列表中的第一个商品图片或标题'); // 6. 在商品详情页加入购物车 await page.waitForSelector('.product-detail', { state: 'visible' }); // 混合等待:视觉+传统选择器 await ms.aiTap('“加入购物车”按钮'); // 7. 进行视觉断言:验证购物车角标数字变为1 const assertionResult = await ms.aiAssert('购物车图标右上角有一个红色的数字“1”'); if (assertionResult.success) { console.log('✅ 测试通过:商品成功加入购物车!'); } else { console.error('❌ 测试失败:购物车数量未更新。'); console.error('断言详情:', assertionResult.details); } // 8. 也可以使用更精确的原子API进行混合测试 // 例如,我们既用视觉点击了按钮,又想用传统方式获取DOM元素验证某个属性 const cartCountElement = await page.$('.cart-count'); const countText = await cartCountElement.innerText(); console.log(`购物车数量(通过DOM获取): ${countText}`); } catch (error) { console.error('测试执行过程中发生错误:', error); // 这里可以结合Midscene的截图和报告功能,保存错误时的现场 await page.screenshot({ path: `error-${Date.now()}.png` }); } finally { // 9. 清理资源 await browser.close(); } })();

4.3 关键步骤解析与避坑指南

  1. 混合等待策略:纯粹的视觉测试在等待页面状态变化时可能不够精确。例子中我们结合了page.waitForTimeout(不推荐在生产中使用)和page.waitForSelector。更好的实践是利用Midscene.js的aiAssert来等待某个视觉元素出现,或者结合Playwright的page.waitForFunction来等待某个DOM状态。
  2. 指令的精确性与模糊性aiActaiTap中的自然语言描述需要在“精确”和“泛化”之间平衡。“顶部的搜索框”比“搜索框”更精确,但可能在不同页面布局下失效。如果网站有固定的>// utils/midsceneHelper.js const { midscene } = require('@midscene/sdk'); class MidsceneHelper { constructor(page) { this.page = page; this.ms = null; } async init() { this.ms = await midscene.init({ page: this.page, apiKey: process.env.MIDSCENE_API_KEY, }); return this; } async visuallyTap(description) { if (!this.ms) throw new Error('Midscene not initialized'); return await this.ms.aiTap(description); } async visuallyAssert(description) { if (!this.ms) throw new Error('Midscene not initialized'); const result = await this.ms.aiAssert(description); if (!result.success) { throw new Error(`Visual assertion failed: ${description}. Details: ${JSON.stringify(result.details)}`); } return true; } } module.exports = MidsceneHelper; // test/e2e/checkout.spec.js const { test, expect } = require('@playwright/test'); const MidsceneHelper = require('../utils/midsceneHelper'); test.describe('购物流程E2E测试', () => { let msHelper; test.beforeEach(async ({ page }) => { msHelper = await new MidsceneHelper(page).init(); await page.goto('/'); }); test('用户可以使用视觉驱动完成商品购买', async ({ page }) => { // 使用混合模式:传统操作导航,视觉操作核心流程 await page.click('a[href="/products"]'); await msHelper.visuallyTap('“无线耳机”分类'); await msHelper.visuallyTap('第一个标有“限时折扣”的商品'); await msHelper.visuallyTap('“立即购买”按钮'); // 在结算页,视觉识别地址选择等复杂组件 await msHelper.visuallyAssert('页面标题包含“确认订单”'); await msHelper.visuallyTap('默认收货地址旁边的“选择”按钮'); await msHelper.visuallyTap('“提交订单”按钮'); // 最终断言 await expect(page).toHaveURL(/order-success/); await msHelper.visuallyAssert('出现“支付成功”的绿色提示框'); }); });

    5.2 使用YAML定义可读性更强的测试流程

    对于不喜欢写太多代码的QA或产品经理,Midscene.js支持用YAML来定义测试流程。这种声明式的语法更接近自然语言,可读性极高。

    # test-flows/search-and-purchase.yaml name: "搜索并购买商品流程" description: "用户搜索商品并完成购买" steps: - action: navigate url: "https://demo-ecommerce.example.com" - action: aiAct instruction: "在顶部搜索框输入‘智能手机’并搜索" - action: wait timeout: 2s - action: aiTap locator: "搜索结果中品牌为‘品牌A’的第一个商品" - action: aiAssert locator: "商品详情页的‘加入购物车’按钮" condition: "isVisible" - action: aiTap locator: "“加入购物车”按钮" - action: aiAssert locator: "页面顶部购物车图标" condition: "hasText" expectedText: "1"

    然后,你可以使用Midscene CLI或编写一个简单的运行器来执行这个YAML文件。

    5.3 搭建可持续集成的视觉测试流水线

    将Midscene.js集成到CI/CD(如GitHub Actions, GitLab CI)中,可以实现自动化的视觉回归测试。

    核心思路是:

    1. 基线管理:在功能开发完成且UI确认无误时,运行测试并保存一组“基线截图”或“基线测试结果”。这些基线代表了“正确”的状态。
    2. 变更检测:后续每次代码提交或合并请求(PR)时,CI流水线自动运行相同的Midscene.js测试套件。
    3. 差异对比:将本次运行的截图/结果与基线进行对比。Midscene.js的报告功能可以高亮出视觉差异或步骤失败。
    4. 结果反馈:将测试报告(尤其是可视化报告)附到PR评论中,让开发者直观地看到UI变更是否影响了核心功能。

    在CI中,你需要处理无头模式运行、可能更长的超时时间(因为AI推理需要时间)、以及如何存储和对比基线数据等问题。

    6. 常见问题、挑战与优化策略

    即便有了强大的工具,在实际落地过程中依然会遇到各种挑战。以下是我在实践和社区讨论中总结的一些常见问题及应对策略。

    6.1 视觉识别不稳定/不准确

    这是最常见的问题。表现为同一个指令,有时能成功,有时失败。

    • 可能原因与对策
      • UI动态加载:元素尚未完全出现或样式未加载完,AI就进行了识别。对策:在关键操作前增加明确的等待,使用aiAssert等待目标元素出现,或结合Playwright的waitForLoadState
      • 描述模糊:“那个按钮”这种描述在复杂页面中极易歧义。对策:使用更精确的描述,结合元素的相对位置(“表单下方的蓝色提交按钮”)、邻近文本(“紧挨着‘用户名’标签的输入框”)或唯一标识(“ID为‘submit-btn’的按钮”,AI可以识别屏幕上的文字)。
      • UI变化:同一功能在不同状态下的视觉表现不同(如按钮禁用时为灰色)。对策:在测试用例中考虑不同状态,或者使用更稳定的参照物进行定位。
      • 模型能力限制:对于极其抽象或非标准的图标,模型可能无法理解。对策:对于这类关键元素,可以回退到使用传统的、基于属性的选择器进行定位,采用混合测试策略。

    6.2 测试执行速度慢

    由于涉及截图、网络请求(调用AI API)、模型推理,视觉测试比传统测试慢一个数量级。

    • 优化策略
      • 作用域截图:不要总是截取全屏。如果知道目标元素的大致区域,可以只截取页面的一部分传给模型,减少数据传输量和模型处理范围。
      • 缓存与复用:对于在同一个测试会话中重复出现的、静态的UI部分(如导航栏),其识别结果可以考虑在内存中缓存一段时间,避免重复识别。
      • 并行化:确保你的测试框架支持用例级别的并行执行。虽然单个用例慢,但并行跑多个用例可以充分利用计算资源,缩短整体反馈时间。
      • 关键路径优先:不是所有测试都需要视觉驱动。将视觉测试集中在那些真正需要它的、业务价值最高的核心流程上。

    6.3 测试用例的维护成本

    虽然不用维护选择器了,但自然语言描述和“基线”依然需要维护。

    • 维护策略
      • 描述模板化:为常见的UI模式(模态框、表单、数据表格)建立描述模板。例如,表单提交按钮的描述可以统一为“【表单名称】表单底部的‘提交’按钮”。
      • 基线版本化管理:将基线截图和测试用例YAML文件一同放入代码仓库,进行版本控制。UI更新时,需要有计划地更新基线,并将其作为代码审查的一部分。
      • 建立“视觉测试专用环境”:确保测试在一个UI稳定、数据可控的环境(如预发布环境)中运行,避免因无关的数据变化(如推荐商品列表变化)导致测试失败。

    6.4 复杂交互与状态判断

    有些交互不仅仅是点击和输入,比如拖拽、画图、长按等。

    • 当前方案与局限:Midscene.js的核心API(aiTap,aiType)主要覆盖基础操作。对于复杂手势,可能需要结合底层驱动(如Playwright的page.mouse)来模拟,或者等待未来Midscene.js提供更高级的aiDrag等API。状态判断方面,aiAssert可以判断元素是否存在、是否可见、包含何种文本,但对于更复杂的视觉状态(如进度条走到50%),可能需要自定义图像比对逻辑。

    6.5 成本控制

    使用商业AI模型API会产生费用。

    • 成本控制策略
      • 多用本地/开源模型:对于内部系统或可以接受稍低准确率的场景,优先使用可自托管的开源模型(如Qwen)。
      • 优化调用频率:避免在循环或快速重复的操作中使用视觉识别。在一次操作中识别多个相关元素(如果API支持)。
      • 设置预算与告警:在云服务商处为API密钥设置每月用量预算和告警。
      • 分层测试策略:单元测试和接口测试用传统快速方法,只有E2E流程中的关键UI交互才使用视觉驱动。

    7. 总结与展望:视觉驱动自动化的未来

    Midscene.js代表的视觉驱动自动化,正在将UI测试从“代码实现细节”的泥潭中解放出来,推向一个更接近用户真实感知、更适应现代复杂应用架构的新阶段。它的价值不仅在于解决了Canvas、跨域iframe等特定技术难题,更在于提供了一种以“视觉”为统一抽象层的测试方法论,这为多端统一测试、低代码/无代码测试、以及AI智能体自主测试打开了想象空间。

    从我个人的实践来看,现阶段它并非银弹,最适合的场景是作为传统自动化测试框架的“强力补充”,用于攻克那些选择器无能为力的“硬骨头”,以及构建核心业务流程的、对UI变化相对不敏感的端到端测试。成功的落地关键在于“混合策略”:在正确的地方使用正确的工具。

    未来,随着多模态大模型能力的持续进化,视觉定位的精度和速度会进一步提升,成本也会下降。我们可以期待更复杂的交互指令(如“将这个文件图标拖到回收站”)、更智能的上下文理解(如“完成这个多步骤表单的填写”)、以及更深入的与开发流程的集成(如根据UI设计稿自动生成测试用例)。Midscene.js已经迈出了坚实的第一步,而这场由视觉智能驱动的测试变革,才刚刚开始。对于任何面临复杂UI测试挑战的团队,现在正是深入了解并尝试这项技术的好时机。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/7 19:38:35

告别手动操作!英雄联盟玩家必备的本地化智能工具实战指南

告别手动操作&#xff01;英雄联盟玩家必备的本地化智能工具实战指南 【免费下载链接】League-Toolkit An all-in-one toolkit for LeagueClient. Gathering power &#x1f680;. 项目地址: https://gitcode.com/gh_mirrors/le/League-Toolkit 还在为BP阶段手忙脚乱而错…

作者头像 李华
网站建设 2026/7/7 19:36:24

Laravel 5.8 SQL注入漏洞深度解析:从Unique规则到CVE-2021-39165

Laravel 5.8 SQL注入漏洞深度解析&#xff1a;从Unique规则到CVE-2021-39165Laravel作为全球最流行的PHP框架之一&#xff0c;其安全性一直备受开发者关注。然而&#xff0c;2019年曝光的Laravel 5.8 SQL注入漏洞&#xff08;CVE-2021-39165&#xff09;揭示了框架验证机制中隐…

作者头像 李华
网站建设 2026/7/7 19:35:43

JSch 0.1.54 连接 OpenSSH 8.5 算法协商失败:3种解决方案与安全权衡

JSch 0.1.54 连接 OpenSSH 8.5 算法协商失败&#xff1a;3种解决方案与安全权衡当Java开发者使用JSch库进行SFTP文件传输时&#xff0c;遇到Algorithm negotiation fail错误是令人头疼的常见问题。这个问题通常发生在较旧版本的JSch&#xff08;如0.1.54&#xff09;尝试连接较…

作者头像 李华
网站建设 2026/7/7 19:33:15

8G显存本地部署AI漫剧生成:基于ComfyUI的自动化工作流搭建指南

&#x1f680; 30款热门AI模型一站整合&#xff0c;DeepSeek/GLM/Qwen 随心用&#xff0c;限时 5 折。 &#x1f449; 点击领海量免费额度 在实际 AI 内容创作领域&#xff0c;本地部署一个能够自动生成角色、分镜和视频的 AI 漫剧工作流&#xff0c;已经成为很多开发者和小…

作者头像 李华
网站建设 2026/7/7 19:30:59

如何高效构建个人离线小说图书馆:novel-downloader技术解析

如何高效构建个人离线小说图书馆&#xff1a;novel-downloader技术解析 【免费下载链接】novel-downloader 一个可扩展的通用型小说下载器。 项目地址: https://gitcode.com/gh_mirrors/no/novel-downloader 在数字内容瞬息万变的时代&#xff0c;小说爱好者们经常面临一…

作者头像 李华