
1. 项目概述为什么我们需要 Midscene.js最近在跟几个测试团队的朋友聊天发现大家普遍有个痛点传统的自动化测试脚本维护成本太高了。页面元素一变脚本就得跟着改一个回归测试跑下来光是处理各种定位失败、等待超时的问题就能耗掉大半天。更别提那些复杂的业务流写脚本的逻辑比开发业务代码还绕。这时候AI 驱动的自动化测试工具开始进入视野。它们号称能“看懂”界面自动适应变化听起来很美但上手一试问题也不少。要么是云端服务数据安全心里没底要么是集成复杂本地环境搞不定再或者就是生成的脚本可读性差出了问题都不知道从哪查起。直到我深度折腾了Midscene.js才感觉找到了一个比较理想的平衡点。它不是一个庞大的平台而是一个开源的 Node.js 库核心思路很清晰利用多模态大模型比如 GPT-4V的视觉理解能力将自然语言指令直接转化为对浏览器页面的自动化操作。你可以告诉它“点击那个蓝色的登录按钮”或者“在搜索框里输入‘手机’然后按回车”它就能尝试去理解和执行而不是依赖脆弱的 XPath 或 CSS Selector。这带来的改变是根本性的。测试用例可以用更接近人类思维的自然语言来描述脚本的编写和维护门槛大大降低。对于需要快速验证的探索性测试、或者页面结构频繁变动的早期项目Midscene.js 提供了一种全新的、更灵活的自动化可能性。它特别适合前端开发者自测、测试工程师构建智能测试辅助工具或者任何想尝试将 AI 能力低成本、高可控地集成到本地工作流中的团队。2. 核心设计思路Midscene.js 是如何工作的要玩转 Midscene.js不能只停留在调用 API 的层面得先理解它底层的运作机制。这样当遇到执行偏差或者性能问题时你才知道该从哪个环节去排查和优化。2.1 架构拆解从指令到动作的旅程Midscene.js 的核心流程可以概括为“观察-思考-执行”的循环这非常像一个人工测试员在操作电脑。观察Screenshot Context当你下达一个指令如click “提交” button后Midscene.js 首先会命令 Puppeteer 或 Playwright 对当前浏览器页面进行截图。但这还不够它还会收集当前页面的文本上下文。这个上下文通常是通过提取页面的可访问性树Accessibility Tree或者主要文本节点来获得的包含了按钮文字、链接文本、标签等所有可见文本信息。截图提供了视觉布局文本上下文提供了语义信息两者结合构成了 AI 理解页面的“眼睛”。思考LLM Reasoning截图和文本上下文会被一起打包发送给你配置的多模态大模型例如 OpenAI 的 GPT-4 with vision。你的自然语言指令也会一并发送。模型的任务是进行“视觉问答”VQA基于看到的画面和文字理解指令的意图并精准定位到需要操作的元素。它的输出不是一个简单的坐标而是一个结构化的操作计划通常包括操作类型click,type,hover,select等。元素定位描述一个基于画面和文本的自然语言描述例如“那个位于‘用户名’输入框下方、带有‘登录’文字的蓝色矩形按钮”。操作参数对于type操作就是需要输入的文本。注意这里的一个关键点是模型并不直接输出浏览器可执行的 DOM 选择器。它输出的是人类可读的描述。这意味着 Midscene.js 本身不依赖于任何具体的页面实现技术React, Vue 等只要模型能“看”懂它就能操作。执行Action ExecutionMidscene.js 拿到模型返回的操作计划后需要将其“翻译”成浏览器能执行的动作。这一步通常通过计算机视觉CV或文本匹配来实现。例如对于“点击‘登录’按钮”它可能会在截图或文本上下文中寻找与“登录”文本最匹配的元素然后计算出该元素在屏幕上的坐标最后通过 Puppeteer/Playwright 的 API 模拟鼠标点击该坐标。2.2 方案选型考量为什么是 Midscene.js 而不是其他市面上 AI 测试方案不少为什么我会花时间研究这个库主要是基于以下几点权衡可控性 vs. 易用性许多成熟的 AI 测试平台如某些云测平台提供的 AI 功能开箱即用但成了黑盒。测试数据、业务逻辑都要上传到云端对于金融、医疗等对数据敏感的行业这是不可接受的。Midscene.js 运行在本地你可以自己掌控从截图到 API 调用的整个链条数据不出私域心里踏实。成本与灵活性作为开源库Midscene.js 本身免费。成本主要来自调用大模型 API如 GPT-4V的费用。这看似是缺点实则是优点。你可以自由选择不同模型供应商OpenAI, Anthropic, 国内合规的模型服务商等根据任务复杂度在效果和成本间做权衡例如简单任务用便宜的模型复杂任务用强模型。这种按需付费、自主选择的模式比订阅一个固定功能的 SaaS 平台通常更灵活、长期看可能更经济。集成与扩展它是一个 Node.js 库可以无缝集成到现有的 CI/CD 流水线Jenkins, GitLab CI, GitHub Actions中也能和你用 Mocha、Jest、Playwright Test 写的传统用例共存。你可以用它处理那些变化频繁、难以定位的模块而稳定的核心流程仍用传统脚本形成“AI传统”的混合自动化策略性价比最高。技术栈亲和度对于前端和 Node.js 技术栈的团队来说Midscene.js 基于 Puppeteer/Playwright学习曲线平缓调试工具链Chrome DevTools也是大家熟悉的出了问题更容易排查。3. 环境搭建与核心配置实战理论讲完我们动手搭一个真正能跑起来的、高效的环境。这里我会分享一个我优化过的配置方案兼顾了执行速度、稳定性和成本。3.1 基础环境准备首先确保你的系统已经安装 Node.js (建议 v18 或以上) 和 npm/yarn。然后创建一个新的项目目录。mkdir midscene-test-project cd midscene-test-project npm init -y接下来安装核心依赖。Midscene.js 默认使用 Playwright 作为浏览器驱动因为它对现代 Web 技术的支持更好且自带浏览器内核无需单独安装 Chrome。npm install midscene playwright # Playwright 需要安装其自带的浏览器 npx playwright install chromium3.2 大模型 API 配置关键步骤这是 Midscene.js 的“大脑”配置直接决定其智能程度。我们以 OpenAI GPT-4 Turbo with vision 为例因为它目前在多模态理解和指令跟随上表现最均衡。获取 API Key前往 OpenAI 平台创建 API Key。妥善保管不要提交到代码仓库。创建环境变量文件在项目根目录创建.env文件。OPENAI_API_KEYsk-your-actual-api-key-here # 可选如果你需要通过代理访问请注意此处仅指企业内网或合规的网络代理服务用于访问国际互联网服务绝对不涉及任何违规翻墙行为 # HTTPS_PROXYhttp://your-corporate-proxy:port配置 Midscene Client创建一个配置文件例如config/midscene.config.js。这里我会加入一些优化参数。import { Midscene } from midscene; import { chromium } from playwright; import dotenv from dotenv; dotenv.config(); // 加载 .env 文件中的环境变量 export async function createMidsceneClient() { // 1. 启动浏览器推荐使用无头模式headless: true在CI中运行调试时可设为 false const browser await chromium.launch({ headless: true, args: [--disable-dev-shm-usage, --no-sandbox] // 这些参数有助于在Docker或内存有限的环境中稳定运行 }); const page await browser.newPage(); // 2. 设置页面视口让AI看到的画面和人类一致 await page.setViewportSize({ width: 1280, height: 720 }); // 3. 创建 Midscene 客户端 const client new Midscene({ page, // 传入 Playwright 的 page 对象 apiKey: process.env.OPENAI_API_KEY, model: gpt-4-turbo, // 指定使用支持视觉的模型 // 以下为优化参数 maxTokens: 500, // 限制响应长度控制成本 temperature: 0.1, // 低温度值使模型输出更确定、更稳定减少“胡思乱想” reasoningEffort: low, // 如果使用 o1 系列模型可设置平衡速度与成本 // 超时和重试配置 actionTimeout: 30000, // 单次动作执行超时时间毫秒 maxRetries: 2, // 操作失败时重试次数 }); return { client, browser, page }; }实操心得temperature参数对测试自动化至关重要。设为较低值如0.1-0.3可以大幅提高模型执行相同指令时输出操作计划的一致性避免因为模型的随机性导致脚本时而过、时不过这是保证测试稳定性的一个关键技巧。3.3 编写你的第一个 AI 自动化测试脚本让我们写一个简单的测试用例打开百度首页搜索“Midscene.js”。创建文件test/first-ai-test.js。import { createMidsceneClient } from ../config/midscene.config.js; (async () { const { client, browser, page } await createMidsceneClient(); try { console.log( 开始 AI 自动化测试...); // 步骤 1: 导航到目标页面 await page.goto(https://www.baidu.com); // 给页面一点加载时间AI也需要“看”清楚 await page.waitForTimeout(2000); // 步骤 2: 使用自然语言命令 AI 在搜索框输入内容 console.log(输入搜索关键词...); await client.action(在搜索框里输入“Midscene.js”); // 等待一下让输入完成 await page.waitForTimeout(1000); // 步骤 3: 使用自然语言命令 AI 点击“百度一下”按钮 console.log(点击搜索按钮...); await client.action(点击“百度一下”按钮); // 步骤 4: 等待搜索结果页面加载 await page.waitForSelector(#content_left, { timeout: 10000 }); await page.waitForTimeout(3000); // 等待结果渲染 // 步骤 5: 可选让AI验证结果。这是一个更复杂的指令。 console.log(验证搜索结果...); const result await client.action(当前页面是否出现了与“自动化测试”相关的文字如果有请告诉我第一处出现的完整句子。); console.log(AI 验证结果:, result); console.log(✅ 测试流程执行完毕); } catch (error) { console.error(❌ 测试执行失败:, error); } finally { // 步骤 6: 关闭浏览器释放资源 await browser.close(); } })();运行这个脚本node test/first-ai-test.js如果一切配置正确你将看到浏览器自动打开或无头运行完成搜索操作并在控制台输出 AI 对结果的判断。第一次成功运行的那一刻你会真切感受到 AI 驱动自动化的魔力。4. 进阶实战构建健壮的 AI 测试用例与工作流基础的跑通只是第一步。要把 Midscene.js 用于实际项目我们需要解决稳定性、可维护性和集成性问题。4.1 编写稳定可靠的 AI 测试用例直接使用client.action(‘描述’)虽然简单但在复杂场景下容易失败。我们需要更精细的控制。策略一混合定位提升稳定性完全依赖 AI 识别在元素密集或动态加载区域可能不准。我们可以结合传统定位方式。// 不好的方式完全依赖AI在复杂表格中找“编辑”按钮 // await client.action(点击第一行产品的编辑按钮); // 更好的方式先用Playwright定位到表格行再让AI在该区域操作 const firstRow await page.locator(.product-table tbody tr).first(); // 假设我们让AI在 firstRow 这个元素代表的截图区域内操作 // 注Midscene.js可能需要扩展或使用其高级API来支持指定操作区域这是一个重要的实践方向。 // 当前思路可以先对特定区域截图然后结合上下文发送给AI。这需要更底层的调用。策略二分步引导降低AI理解难度不要给AI一个复杂的长指令。拆分成原子操作。// 复杂指令容易出错 // await client.action(在顶部的导航栏找到“用户管理”点击它然后在打开的新页面中找到搜索框输入“张三”点击搜索); // 拆解后的稳定指令 await client.action(点击页面上方的“用户管理”菜单); await page.waitForURL(**/user-management); // 等待页面跳转 await client.action(在页面上找到搜索框); await client.action(在搜索框里输入“张三”); await client.action(点击搜索按钮);策略三加入明确验证点自动化测试的灵魂是验证。除了让AI执行更要让AI判断。// 执行登录操作 await client.action(在用户名输入框输入“testuser”); await client.action(在密码输入框输入“password123”); await client.action(点击登录按钮); await page.waitForNavigation(); // 等待跳转 // 验证登录成功 - 让AI“看”页面并做出判断 const loginCheck await client.action(当前页面是否显示“欢迎回来testuser”或“登录成功”之类的提示请只回答“是”或“否”。); if (loginCheck.toLowerCase().includes(是)) { console.log(✅ 登录成功验证通过); } else { throw new Error(❌ 登录成功提示未出现可能登录失败); }4.2 集成到现有测试框架以 Jest 为例为了让 AI 测试用例能被项目管理、生成报告需要集成进测试框架。安装 Jestnpm install --save-dev jest jest-playwright-preset配置 Jest创建jest.config.jsmodule.exports { preset: jest-playwright-preset, testEnvironment: ./config/custom-test-environment.js, // 自定义环境用于初始化Midscene setupFilesAfterEnv: [./config/jest-setup.js], testMatch: [**/__tests__/**/*.test.js], };创建自定义测试环境config/custom-test-environment.jsconst NodeEnvironment require(jest-environment-node).TestEnvironment; const { createMidsceneClient } require(./midscene.config.js); class CustomTestEnvironment extends NodeEnvironment { async setup() { await super.setup(); // 在每个测试文件执行前创建浏览器和Midscene客户端 const { client, browser, page } await createMidsceneClient(); this.global.browser browser; this.global.page page; this.global.client client; } async teardown() { // 在每个测试文件执行后关闭浏览器 if (this.global.browser) { await this.global.browser.close(); } await super.teardown(); } } module.exports CustomTestEnvironment;编写 Jest 测试用例__tests__/ai-search.test.jsdescribe(百度搜索 AI 自动化测试, () { beforeAll(async () { // 环境已由 CustomTestEnvironment 设置好 this.client global.client; this.page global.page; }); it(应该能通过AI指令完成搜索并看到结果, async () { await this.page.goto(https://www.baidu.com); await this.page.waitForTimeout(2000); await this.client.action(在搜索框输入“Jest自动化测试”); await this.page.waitForTimeout(1000); await this.client.action(点击“百度一下”按钮); await this.page.waitForSelector(#content_left, { timeout: 10000 }); // 使用Jest的expect进行断言结合AI的判断 const searchResult await this.client.action(页面主体内容区域是否包含了“Jest”这个词请只回答“包含”或“不包含”。); expect(searchResult).toContain(包含); // 断言AI的反馈 }, 60000); // 设置较长的超时时间因为AI操作可能较慢 });运行测试npx jest __tests__/ai-search.test.js这样你的 AI 测试用例就能像普通单元测试一样运行并生成标准的测试报告了。4.3 搭建 CI/CD 流水线示例GitHub Actions将 AI 自动化测试接入持续集成确保每次代码变更都得到验证。创建.github/workflows/ai-test.ymlname: AI 自动化测试 on: push: branches: [ main, develop ] pull_request: branches: [ main ] jobs: test: runs-on: ubuntu-latest timeout-minutes: 30 # AI测试可能较慢设置长超时 steps: - uses: actions/checkoutv3 - name: Setup Node.js uses: actions/setup-nodev3 with: node-version: 18 - name: Install dependencies run: npm ci - name: Install Playwright Browsers run: npx playwright install chromium - name: Run AI Automation Tests env: OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} # 在GitHub仓库Settings/Secrets中配置 run: npx jest __tests__/ --ci --maxWorkers2 --testTimeout120000 # 增加单个测试超时 - name: Upload Playwright trace (on failure) if: failure() uses: actions/upload-artifactv3 with: name: playwright-traces path: test-results/5. 常见问题、性能优化与避坑指南在实际使用中你肯定会遇到各种问题。下面是我踩过坑后总结的实战经验。5.1 常见问题与排查技巧问题现象可能原因排查与解决思路AI 执行动作失败(如点击错位置)1. 页面未完全加载/动态内容未稳定。2. 指令描述模糊存在歧义。3. 模型“看”到的截图分辨率或内容不对。1. 在action前增加page.waitForTimeout或等待特定元素。2. 优化指令使用更唯一、精确的描述如“点击蓝色的、带有‘提交’文字的按钮”。3. 在失败时手动保存截图 (await page.screenshot({ path: ‘debug.png’ }))检查AI看到的画面是否和你预期一致。API 调用超时或报错1. 网络问题。2. OpenAI API 额度用尽或限速。3. 请求的 tokens 超长截图太大。1. 检查网络连通性考虑增加actionTimeout。2. 检查 OpenAI 账户仪表盘。3. 优化截图范围或分辨率或在发送前压缩图片。Midscene.js 可能内置处理也可查阅其高级配置。执行速度非常慢1. 多模态大模型推理本身慢。2. 网络延迟高。3. 未使用无头模式。1. 这是固有瓶颈。对于复杂流程考虑混合模式关键、易变的操作用AI稳定、固定的流程用传统脚本。2. 选择地理上更近的 API 端点如果服务商提供。3. 确保生产环境使用headless: true。成本增长过快1. 测试用例多频繁调用昂贵模型如 GPT-4V。2. 每次action都发送全屏大图。1.分层测试核心冒烟测试用AI大量回归测试用传统自动化。2.模型降级对简单、重复的识别任务尝试使用更便宜的小模型如 GPT-4o-mini 或专精OCR的本地模型。3.缓存与复用对于不变页面的相同操作可以考虑缓存AI返回的操作计划但需谨慎页面变化会导致缓存失效。无法处理非可视元素AI 依赖视觉无法操作隐藏元素、文件上传(input type“file”)等。对于文件上传等非标准交互回退到 Playwright 原生方法。这是混合自动化的典型场景。脚本在不同环境不一致浏览器视口大小、屏幕分辨率、字体渲染差异导致AI“看”到的画面不同。在 CI 和本地都固定浏览器视口大小如setViewportSize({ width: 1280, height: 720 })。使用 Docker 容器化测试环境以保证一致性。5.2 性能优化与成本控制实战技巧指令工程优化这是提升成功率、降低 token 消耗最有效的方法。具体化用“点击‘保存草稿’按钮”代替“点击保存按钮”。结构化对于列表操作可以告诉AI规则如“点击第一个商品下方的‘加入购物车’按钮”。分步引导如前所述将复杂任务拆解。截图优化// 在创建Midscene客户端时可以探索是否支持传递截图选项 // 例如只截取页面主体部分排除固定导航栏/页脚减少无关信息干扰AI // 这需要查阅Midscene.js的高级API或修改其源码。思路是在调用AI前先使用Playwright对特定区域截图。 const contentArea await page.locator(‘#main-content’); const screenshotBuffer await contentArea.screenshot(); // 然后将 screenshotBuffer 和指令一起发送给AI需自定义调用逻辑实现智能重试与降级机制async function robustAIAction(client, instruction, fallbackSelector null, maxRetries 2) { for (let i 0; i maxRetries; i) { try { await client.action(instruction); return; // 成功则退出 } catch (error) { console.warn(AI 指令执行失败 (第 ${i 1} 次): ${error.message}); if (i maxRetries - 1) { // 最后一次重试也失败尝试降级方案 if (fallbackSelector) { console.log( 降级使用传统选择器 ${fallbackSelector}); await page.click(fallbackSelector); } else { throw error; // 没有降级方案抛出错误 } } await page.waitForTimeout(1000); // 重试前等待 } } } // 使用示例 await robustAIAction( client, ‘点击登录按钮’, ‘button:has-text(“登录”)’, // 降级用的Playwright选择器 2 );批量操作与并发控制对于需要操作大量相似元素的场景如勾选列表所有复选框不要让AI一个个识别。可以先让AI识别一个样本然后用 Playwright 的locatorAPI 批量处理。// 1. 让AI先找到一个样本元素例如第一个复选框 // 假设我们通过某种方式获得了该样本的定位信息这需要Midscene.js提供更详细的返回 // 2. 使用Playwright定位所有同类元素 const allCheckboxes await page.locator(‘.list-item input[type“checkbox”]’); const count await allCheckboxes.count(); for (let i 0; i count; i) { await allCheckboxes.nth(i).check(); }经过这些优化你的 Midscene.js 测试环境将从一个“有趣的实验”转变为能在实际项目中稳定贡献价值的自动化工具。它可能不会完全取代传统自动化但作为处理“变化”和“不确定性”的利器无疑为测试左移和提升研发效能打开了新的大门。