1. 项目概述
最近在搞UI自动化测试的团队,估计都听过一个词:维护成本。传统基于元素定位(比如XPath、CSS Selector)的脚本,页面改个按钮位置、换个class名,测试脚本就得跟着改,一不留神就报错。这种“脆弱性”让自动化测试的投入产出比经常被打上问号。我自己带团队做前端项目,对这点感受太深了。所以,当我看到 Playwright 和 Midscene.js 这两个名字放在一起时,第一反应是:这可能是解决“定位之痛”的一个新思路。简单说,Playwright是微软出的一个现代浏览器自动化库,功能强大,而Midscene.js则是一个“视觉驱动”的UI自动化库,它让AI“看懂”屏幕,然后用自然语言去操作。把它们结合起来,意味着我们写测试脚本时,可能不再需要去精确地定位一个按钮的XPath,而是直接告诉AI:“点击那个蓝色的登录按钮”。这听起来有点科幻,但实际用下来,它确实在改变我们编写和维护UI自动化测试的方式。
2. 核心思路与技术选型解析
2.1 为什么是Playwright + Midscene.js?
传统的UI自动化测试框架,无论是Selenium、Cypress还是Playwright本身,其核心操作模式都是“定位+动作”。你需要先通过选择器(Selector)找到页面上的元素,然后对这个元素执行点击、输入等操作。这套模式的瓶颈在于“定位”环节。前端页面是动态的,UI库升级、A/B测试、甚至开发人员重构代码,都可能导致选择器失效。维护这些选择器成了测试工程师的沉重负担。
Playwright 在这个传统赛道里已经是佼佼者。它支持多浏览器(Chromium, Firefox, WebKit),API设计现代,自带强大的录制、调试和并行测试能力。但它依然遵循“定位+动作”的范式。
Midscene.js 引入了一个全新的范式:“视觉+理解+动作”。它不关心底层的DOM结构,而是通过截取屏幕图像,利用多模态大模型(比如GPT-4V, Claude 3, 或开源的视觉模型)来“理解”屏幕上有什么。你可以用自然语言描述你想操作的目标(如“搜索框”、“购物车图标”),Midscene.js的AI Agent会分析截图,找到目标位置,并驱动Playwright去执行点击、输入等操作。
两者的结合点 在于,Midscene.js需要一个底层的“执行器”来实际操控浏览器。Playwright正是这样一个强大、稳定的执行器。Midscene.js负责“看”和“想”(视觉感知与规划),Playwright负责“做”(执行浏览器操作)。这种分工带来了几个关键优势:
- 脚本健壮性提升 :只要UI视觉变化不大(按钮还是那个样子,位置差不多),即使底层HTML结构翻天覆地,基于自然语言的指令依然有效。
- 开发效率飞跃 :测试用例可以用近乎业务语言来描述,降低了编写门槛。对于复杂交互,AI能自动规划步骤,减少了手动编排操作序列的工作。
- 跨平台一致性 :视觉识别不依赖于特定的前端框架或平台原生控件,理论上同一套自然语言指令可以适配Web、移动端甚至桌面应用(需Midscene支持相应平台)。
2.2 技术架构与工作流程
理解这套组合拳如何工作,对后续使用和问题排查至关重要。其核心工作流程可以拆解为以下几个步骤:
-
启动与配置
:你的测试脚本启动Playwright,打开一个浏览器(可以是本地或远程)。同时,初始化Midscene.js的
PlaywrightAgent,并将Playwright的page对象传递给它。你需要配置好AI模型(如OpenAI的GPT-4V或本地部署的视觉模型)的API地址和密钥。 - 导航与截图 :通过Playwright导航到目标页面。当需要执行AI操作时,Midscene.js会通过Playwright的API捕获当前页面的截图。
-
视觉理解与规划
:Midscene.js将截图和你的自然语言指令(如
aiAct('type “Headphones” in search box, hit Enter'))一起发送给配置的AI模型。模型会分析图像,理解指令意图,并规划出具体的操作步骤序列。例如,它可能先识别出搜索框的位置,然后规划“将光标移动到搜索框”、“输入文本‘Headphones’”、“按下Enter键”等一系列原子操作。 -
动作映射与执行
:Midscene.js将规划出的原子操作(如
click(x, y),type(text))映射成Playwright的API调用。例如,click(x, y)会转换成page.mouse.click(x, y)。然后由Playwright驱动浏览器实际执行这些操作。 -
验证与报告
:操作执行后,Midscene.js可以再次截图,通过
aiAssert或aiQuery等方法,让AI验证结果是否符合预期。整个过程的关键步骤和截图会被自动记录,生成一份图文并茂的HTML报告,方便回溯和调试。
注意 :这个流程中,AI的“规划”能力是关键。对于复杂指令,AI可能需要多次尝试(重规划)才能成功。Midscene.js提供了
replanningCycleLimit参数来控制重试次数。
3. 环境搭建与核心配置详解
纸上谈兵终觉浅,我们直接上手搭建一个可运行的环境。这里我会以Node.js项目为例,涵盖两种主要集成方式:独立脚本和Playwright测试框架集成。
3.1 基础环境准备
首先,确保你的系统已安装Node.js(建议LTS版本)和npm/yarn/pnpm等包管理器。创建一个新的项目目录并初始化。
mkdir playwright-midscene-demo && cd playwright-midscene-demo
npm init -y
3.2 安装依赖
我们需要安装Playwright、Midscene.js的核心库、Playwright的测试运行器(如果做测试集成)以及一个TypeScript运行器(如tsx)。
# 安装核心依赖
npm install playwright @midscene/web --save-dev
# 如果你打算编写TypeScript脚本并直接运行,安装tsx
npm install tsx --save-dev
# 如果你打算集成到Playwright Test框架中,还需要安装测试运行器
npm install @playwright/test --save-dev
# 最后,让Playwright安装它需要的浏览器(主要是Chromium)
npx playwright install chromium
实操心得 :
npx playwright install可能会因为网络问题下载很慢。一个有效的加速方法是使用国内的镜像源。可以设置环境变量来加速:PLAYWRIGHT_DOWNLOAD_HOST="https://npmmirror.com/mirrors/playwright" npx playwright install chromium如果你只做Web测试,安装
chromium这一个浏览器就足够了,能节省大量时间和磁盘空间。
3.3 配置AI模型(最关键的一步)
Midscene.js的强大依赖于背后的多模态大模型。你需要一个能处理图像和文本的模型服务。目前主流的选择有:
- OpenAI GPT-4V / o1 :效果最好,但需要API Key且可能产生费用。
- Claude 3(Opus/Sonnet) :效果同样出色。
- 开源模型 :如Qwen-VL、LLaVA等,可以部署在本地或自有服务器,成本可控。
配置方式是通过环境变量。强烈建议使用
.env
文件来管理,避免密钥泄露。
-
在项目根目录创建
.env文件。 - 根据你的模型服务,填写配置。以下是针对OpenAI和本地开源模型的示例:
示例1:使用OpenAI GPT-4V
# .env
MIDSCENE_MODEL_BASE_URL=https://api.openai.com/v1
MIDSCENE_MODEL_API_KEY=你的OpenAI_API_Key
MIDSCENE_MODEL_NAME=gpt-4-vision-preview # 或 gpt-4o
MIDSCENE_MODEL_FAMILY=openai
示例2:使用本地部署的Ollama(运行Qwen-VL)
# .env
MIDSCENE_MODEL_BASE_URL=http://localhost:11434/v1
MIDSCENE_MODEL_API_KEY=ollama # 如果本地Ollama未设置认证,可填任意非空值
MIDSCENE_MODEL_NAME=qwen2.5-vl:7b # Ollama中的模型名称
MIDSCENE_MODEL_FAMILY=openai # 注意:Ollama兼容OpenAI API,所以这里填openai
-
在你的脚本或测试文件中,使用
dotenv包来加载这些环境变量。如果你安装了tsx,它通常会自动加载项目根目录的.env文件。为了保险起见,可以在脚本开头显式导入:import 'dotenv/config';
注意事项 :模型的选择直接影响到自动化脚本的准确性、速度和成本。GPT-4V精度高但慢且贵,适合对稳定性要求极高的核心场景。开源模型成本低,但可能需要更精确的指令(Prompt)和更长的上下文(Context)。在项目初期,建议先用GPT-4V快速验证流程和脚本可行性,后期再根据实际情况评估是否迁移到成本更低的模型。
4. 两种集成模式实战
Midscene.js与Playwright的集成主要有两种模式,适用于不同场景。
4.1 模式一:独立脚本集成(快速原型与数据抓取)
这种模式适合快速验证想法、编写一次性自动化脚本或进行数据抓取。它不依赖于Playwright的测试框架,更加灵活。
步骤拆解:
-
编写脚本 (
demo.ts) :import { chromium } from 'playwright'; import { PlaywrightAgent } from '@midscene/web/playwright'; import 'dotenv/config'; // 一个简单的睡眠函数,用于等待页面稳定 const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms)); (async () => { // 1. 启动Playwright浏览器 // 设置 headless: false 可以在运行时看到浏览器操作,便于调试 const browser = await chromium.launch({ headless: false }); const page = await browser.newPage(); await page.setViewportSize({ width: 1280, height: 768 }); // 2. 导航到目标页面 await page.goto('https://www.ebay.com'); await sleep(3000); // 等待页面初步加载 // 3. 创建Midscene Agent,绑定到当前页面 const agent = new PlaywrightAgent(page); // 4. 使用自然语言指令进行自动化操作 // AI会“看到”页面,找到搜索框,输入“Headphones”并回车 console.log('开始搜索...'); await agent.aiAct('type "Headphones" in search box, hit Enter'); // 5. 等待AI确认某个状态出现(比固定sleep更智能) await agent.aiWaitFor('there is at least one headphone item on page', { timeoutMs: 10000 }); // 6. 让AI查询页面信息,并结构化返回 console.log('获取商品列表...'); const items = await agent.aiQuery<Array<{ itemTitle: string; price: number }>>( '{itemTitle: string, price: Number}[], find item in list and corresponding price' ); console.log('搜索到的耳机:', items); // 7. 进行一些逻辑判断 const isExpensive = await agent.aiBoolean('Is there any headphone priced over 1000 dollars?'); console.log('是否存在超过1000美元的耳机?', isExpensive); // 8. 执行更多操作,比如点击第一个商品 if (items && items.length > 0) { await agent.aiTap('the first item in the list'); await sleep(2000); // 等待商品详情页加载 // 可以在新页面继续用同一个agent操作,因为Midscene默认会处理同标签页导航 } // 9. 关闭浏览器 await browser.close(); console.log('自动化脚本执行完毕。'); })().catch(console.error); -
运行脚本 :
npx tsx demo.ts运行后,你会看到浏览器自动打开,完成搜索、等待、提取信息、点击等一系列操作,并在控制台输出结果。
-
查看报告 :脚本运行成功后,控制台会输出类似
Midscene - report file updated: /path/to/report/abc123.html的信息。用浏览器打开这个HTML文件,你能看到每一步的屏幕截图、AI执行的指令、以及模型对画面的理解(OCR识别出的文字、定位到的元素等),这对于调试AI行为异常有用。
4.2 模式二:Playwright Test框架集成(企业级UI测试)
如果你已经用Playwright Test来组织你的端到端测试,那么将Midscene.js作为Fixture集成进去是更优雅的方式。它能与Playwright原有的断言、钩子、并行化等特性无缝结合。
步骤拆解:
-
更新Playwright配置 (
playwright.config.ts) :import { defineConfig } from '@playwright/test'; export default defineConfig({ testDir: './e2e', // 测试文件目录 timeout: 120 * 1000, // 超时时间设长一点,AI操作可能较慢 reporter: [ ['list'], // 控制台列表输出 ['@midscene/web/playwright-reporter', { type: 'merged' }] // 集成Midscene报告生成器 ], use: { headless: true, // CI环境通常设为true viewport: { width: 1280, height: 768 }, }, });@midscene/web/playwright-reporter这个Reporter是关键,它负责在测试运行后生成可视化的AI操作报告。 -
创建自定义Fixture (
e2e/fixture.ts) : Fixture是Playwright Test的核心概念,用于封装和复用测试上下文。我们创建一个集成了Midscene AI能力的Fixture。import { test as base } from '@playwright/test'; import type { PlayWrightAiFixtureType } from '@midscene/web/playwright'; import { PlaywrightAiFixture } from '@midscene/web/playwright'; // 扩展基础的test对象,注入AI能力 export const test = base.extend<PlayWrightAiFixtureType>( PlaywrightAiFixture({ // 可选配置:AI执行动作后等待网络空闲的时间,默认2000ms waitForNetworkIdleTimeout: 3000, // 可选配置:AI规划失败后的重试次数,默认30次 replanningCycleLimit: 20, }) ); export { expect } from '@playwright/test'; // 重新导出expect,方便使用 -
编写AI驱动的测试用例 (
e2e/search.spec.ts) :import { test, expect } from './fixture'; // 导入我们自定义的test test.describe('eBay商品搜索测试', () => { test.beforeEach(async ({ page }) => { // 每个测试用例开始前,导航到eBay首页 await page.goto('https://www.ebay.com'); await page.waitForLoadState('networkidle'); }); test('应该能通过AI搜索商品并验证结果', async ({ aiInput, aiTap, aiWaitFor, aiQuery, aiAssert, recordToReport, page }) => { // 1. AI在页面上找到搜索框,并输入“Laptop” await aiInput('Laptop', '搜索框'); // 2. AI找到并点击搜索按钮 await aiTap('搜索按钮'); // 3. 等待AI确认搜索结果页面已加载完成 await aiWaitFor('看到商品列表加载出来', { timeoutMs: 15000 }); // 4. 让AI从页面中提取结构化商品数据 const products = await aiQuery<Array<{ name: string; price: string }>>( '提取前5个商品的名称和价格' ); console.log('提取的商品:', products); // 5. 使用Playwright原生断言验证业务逻辑 expect(products?.length).toBeGreaterThan(0); // 你也可以用AI断言来验证UI状态 await aiAssert('页面顶部显示了“Laptop”相关的筛选条件或面包屑导航'); // 6. 记录当前状态到报告,并添加自定义注释 await recordToReport('搜索结果页验证', { content: `成功搜索到${products?.length}个笔记本电脑商品。` }); // 7. 示例:结合传统定位与AI操作(混合模式) // 如果页面上有一个已知ID的“排序”下拉框,用Playwright精准操作更可靠 await page.selectOption('#sort-select', 'price_asc'); await page.waitForTimeout(1000); // 等待排序生效 // 8. 再次用AI验证排序结果 const firstItemPrice = await aiQuery<string>('第一个商品的价格是多少?'); console.log('排序后第一个商品价格:', firstItemPrice); // 这里可以添加更复杂的价格排序逻辑验证... }); }); -
运行与查看报告 :
# 运行特定测试文件 npx playwright test e2e/search.spec.ts # 或运行所有测试 npx playwright test测试运行后,除了控制台输出,最重要的就是Midscene生成的HTML报告。它不再是简单的通过/失败,而是展示了AI“眼中”的每一步操作和页面状态,对于理解测试为何失败(是定位问题?是页面没加载?还是AI理解错了指令?)有巨大帮助。
5. 高级特性与深度优化
掌握了基础用法后,我们来看看一些能提升稳定性、效率和扩展性的高级技巧。
5.1 处理复杂交互与等待策略
AI模型的速度和网络延迟可能导致操作比传统脚本慢。合理的等待策略至关重要。
-
aiWaitFor是你的主要武器 :它比固定的sleep更智能。AI会持续检查页面状态是否满足描述,一旦满足就继续,避免无谓等待。// 等待一个特定元素出现,或者一个特定状态达成 await agent.aiWaitFor('the login success message pops up', { timeoutMs: 10000 }); // 等待页面从“加载中”变为“加载完成” await agent.aiWaitFor('the page spinner disappears and content is displayed'); -
混合等待
:对于已知的、稳定的网络请求,可以结合Playwright原生的
waitForResponse或waitForLoadState。await aiTap('Submit button'); // 同时等待AI确认和特定API响应 await Promise.all([ agent.aiWaitFor('confirmation message appears'), page.waitForResponse(response => response.url().includes('/api/submit') && response.ok()) ]); -
调整超时与重试
:在创建Agent或Fixture时,根据网络和模型速度调整
waitForNetworkIdleTimeout(动作后等待)和replanningCycleLimit(重规划次数)。对于CI环境,超时可以设长一些。
5.2 自定义动作扩展
Midscene.js内置了点击、输入、滚动等通用动作。但有时你需要更复杂的原子操作。这时可以定义 自定义动作(Custom Action) 。
例如,你需要一个“双击并拖拽”的动作:
import { defineAction, getMidsceneLocationSchema } from '@midscene/core/device';
import { z } from 'zod';
const DoubleClickAndDrag = defineAction({
name: 'doubleClickAndDrag',
description: 'Double click on an element and drag it to another location',
paramSchema: z.object({
source: getMidsceneLocationSchema().describe('The element to double click'),
target: getMidsceneLocationSchema().describe('The destination to drag to'),
}),
async call(param, context) {
const { source, target } = param;
const { page } = context; // 可以从context中获取Playwright的page对象
// 1. 移动到源位置并双击
await page.mouse.move(source.center.x, source.center.y);
await page.mouse.dblclick(source.center.x, source.center.y);
await page.waitForTimeout(200);
// 2. 按下鼠标,移动到目标位置,释放
await page.mouse.down();
await page.mouse.move(target.center.x, target.center.y);
await page.mouse.up();
},
});
// 在创建Agent时注入自定义动作
const agent = new PlaywrightAgent(page, {
customActions: [DoubleClickAndDrag],
});
// 在测试中,AI就可以使用这个新动作了
await agent.aiAct('double click the card and drag it to the trash bin area');
这样,你就将复杂的交互封装成了一个AI可理解和调用的高级指令,极大地增强了脚本的表达能力。
5.3 连接远程浏览器与CI/CD集成
在CI/CD流水线中,我们通常使用Docker容器或无头浏览器服务。Midscene.js可以很好地与远程Playwright浏览器协同工作。
-
连接Browserless或Playwright远程服务 :
import { chromium } from 'playwright'; import { PlaywrightAgent } from '@midscene/web/playwright'; const browser = await chromium.connectOverCDP('wss://chrome.browserless.io?token=YOUR_TOKEN'); const context = browser.contexts()[0] || await browser.newContext(); const page = context.pages()[0] || await context.newPage(); await page.goto('https://your-app.com'); const agent = new PlaywrightAgent(page); // 后续使用方式与本地完全一致这种方式让你可以利用云端的浏览器资源,避免在CI runner上安装和管理浏览器,也使得测试环境更一致。
-
CI中的报告处理 :Midscene生成的HTML报告是独立的文件。在CI中,你需要将其作为产物(Artifact)保存下来。例如,在GitHub Actions中:
- name: Upload Midscene Report uses: actions/upload-artifact@v4 if: always() # 即使测试失败也上传报告 with: name: midscene-html-report path: ./midscene_run/report/**/*.html # Midscene默认的报告输出路径 retention-days: 7这样,每次测试运行后,你都可以下载并查看详细的AI操作报告,快速定位问题。
6. 常见问题排查与性能调优
在实际项目中踩坑是免不了的。下面是我总结的一些典型问题及其解决方案。
6.1 AI指令执行失败或不准
这是最常见的问题。现象可能是AI点击了错误的位置,或者完全没找到目标。
-
问题根因1:指令描述模糊
。“点击那个按钮”可能页面有多个按钮。AI会猜测,可能猜错。
- 解决方案 :使用更精确、唯一的描述。结合上下文元素。例如:“点击登录表单内的蓝色提交按钮”、“点击商品列表右侧的第一个‘加入购物车’按钮”。
-
问题根因2:页面状态未稳定
。AI截图时,页面元素还在加载或动画中。
-
解决方案
:在关键操作前增加
aiWaitFor或适当的sleep,确保目标UI完全渲染。可以先用AI等待一个稳定的视觉状态,如“等待页面加载完成,顶部导航栏出现”。
-
解决方案
:在关键操作前增加
-
问题根因3:模型能力或上下文不足
。使用的开源模型可能对复杂布局或罕见控件识别不准。
-
解决方案
:
- 优化Prompt :在指令中加入更详细的要求,如“找到那个看起来像放大镜图标的搜索框”。
- 升级模型 :如果成本允许,换用更强的模型如GPT-4V。
-
混合定位
:对于极其重要且稳定的元素(如主导航),退一步使用Playwright的
data-testid等测试属性进行精准定位,与AI操作混合使用。
-
解决方案
:
-
问题根因4:动态内容干扰
。弹窗、广告、通知可能会遮挡目标或干扰AI识别。
-
解决方案
:在测试前通过
page.evaluate执行JS代码关闭已知的弹窗,或者调整测试数据避免触发弹窗。也可以让AI先关闭弹窗:“如果看到欢迎弹窗,点击右上角的X关闭它”。
-
解决方案
:在测试前通过
6.2 性能与成本考量
AI模型的调用有延迟和成本,不能像传统脚本那样毫秒级执行成千上万次操作。
-
策略1:分层测试
。不要把所有测试都改成AI驱动。将测试金字塔理论应用到这里:
- 底层(单元测试) :保持不变。
- 中层(集成测试/API测试) :用Playwright传统方式。
- 顶层(端到端UI测试) :用Playwright+Midscene.js覆盖核心、关键的用户旅程(如注册、登录、核心购买流程)。用AI来应对这些流程中UI变化最频繁的部分。
- 策略2:缓存与复用 。Midscene.js支持缓存AI的规划和定位结果。对于相对稳定的页面和操作,启用缓存可以大幅减少模型调用次数和测试时间。查看Midscene文档中关于缓存配置的部分。
-
策略3:指令批处理
。尽可能将多个步骤合并到一个
aiAct指令中,减少截图和模型调用次数。例如,用aiAct('fill in the login form with username “test” and password “123456”, then click the login button')代替三个独立的输入和点击指令。
6.3 报告与调试技巧
Midscene的HTML报告是强大的调试工具,但也要善用。
- 查看“思维链” :报告中会展示AI对截图的分析过程(如果模型支持),这能帮你理解AI为什么做出了某个错误决策,从而优化你的指令。
-
关注截图清晰度
:在高分辨率或缩放屏幕上,如果
deviceScaleFactor设置不当,截图可能模糊,影响AI识别。确保Playwright的视口和缩放设置与测试环境匹配。// 在创建页面时设置正确的deviceScaleFactor const page = await browser.newPage({ viewport: { width: 1280, height: 768 }, deviceScaleFactor: 2, // 根据你的屏幕调整,可通过浏览器控制台输入`window.devicePixelRatio`获取 }); -
处理原生控件
:对于
<select>下拉框,浏览器可能调用操作系统原生控件渲染,导致AI无法“看到”下拉选项。Midscene提供了forceChromeSelectRendering选项来强制Chrome渲染,但会改变样式。如果测试不关心样式,可以开启此选项。
6.4 环境与依赖问题
- Playwright浏览器下载慢 :如前所述,使用国内镜像。
-
截图超时
:在CI环境中,如果遇到
page.screenshot: Timeout ... waiting for fonts to load错误,是因为Playwright在截图时默认等待字体加载。可以设置环境变量PW_TEST_SCREENSHOT_NO_FONTS_READY=1来跳过字体等待。PW_TEST_SCREENSHOT_NO_FONTS_READY=1 npx playwright test -
模型API连接失败
:检查
.env文件配置是否正确,网络是否能访问模型服务地址。对于本地模型,确保服务已启动且端口正确。
将Playwright的稳定执行能力与Midscene.js的视觉智能结合,确实为UI自动化测试打开了一扇新的大门。它不能完全替代基于精准定位的传统测试,但在应对UI频繁变化、快速验证核心流程、降低测试脚本维护成本方面,展现出了独特的价值。从我实际项目的迁移经验来看,最适合的场景是那些业务逻辑稳定但UI交互层变动较大的“端到端用户旅程”测试。初期投入在Prompt工程和稳定性调优上的时间,会在后续数个迭代的维护成本节约中得到回报。当然,它目前还不是银弹,模型成本、执行速度、以及对复杂交互的可靠性都是需要持续权衡和优化的点。我的建议是,从一个具体的、高维护成本的测试用例开始尝试,积累经验,再逐步推广。
1940

被折叠的 条评论
为什么被折叠?



