1. 项目概述为什么前端UI自动化测试值得投入做前端开发的朋友应该都经历过这样的场景每次发版前都要手动把核心功能路径点一遍登录、点击按钮、填写表单、检查弹窗……一套流程下来少说也得十几二十分钟。更头疼的是一旦UI组件库升级或者某个依赖包有变动这种“人肉回归测试”就得再来一遍费时费力还容易遗漏。UI自动化测试就是为了把我们从这种重复劳动中解放出来让机器去执行这些枯燥的点击和断言。但传统的UI自动化测试工具比如Selenium或者基于Puppeteer的框架学习曲线陡峭配置复杂脚本维护成本高。一个简单的点击操作可能要写一堆选择器定位和等待逻辑测试代码比业务代码还长这让很多团队望而却步。Midscene.js的出现正是为了解决这个痛点。它不是一个全新的底层测试引擎而是一个智能化的测试编排与生成层。你可以把它理解为一个“测试副驾驶”它利用AI特别是像GPT-4o这样的多模态大模型的能力去理解你的页面并生成或执行稳定、可读的测试脚本。这个项目的核心价值在于“降本增效”。它瞄准的不是追求极限性能的测试专家而是广大希望引入自动化测试、但又苦于门槛和成本的前端开发团队。通过将AI的视觉理解和自然语言处理能力与测试流程结合Midscene.js试图让编写UI测试像写需求文档一样简单你告诉它“点击登录按钮输入用户名和密码然后验证跳转到首页”它就能帮你生成对应的可执行代码。而标题中提到的“5分钟搞定”并非夸张它指的是从零开始完成一个简单页面的核心流程测试的搭建时间这极大地降低了尝试和启动的门槛。2. 核心思路拆解Midscene.js如何实现“智能化”测试要理解Midscene.js我们需要把它拆解为几个核心组成部分看看它是如何将AI能力融入测试流程的。2.1 架构定位连接器而非替代品首先必须明确Midscene.js本身不直接驱动浏览器。它更像一个智能中间件或适配器。在底层它依赖于成熟的浏览器自动化工具目前主要支持Playwright。Playwright是微软开源的一个非常强大的浏览器自动化库支持Chromium、Firefox和WebKit提供了稳定可靠的API来模拟用户操作。Midscene.js站在Playwright的肩膀上专注于解决“如何更简单地描述测试”和“如何让测试更稳定”这两个问题。它的工作流程通常是这样的你编写用自然语言或简单结构描述的测试场景Midscene.js通过其AI引擎可配置为本地模型或调用如GPT-4o的API将这个描述“翻译”成Playwright代码然后执行这段代码并返回结果。同时它还能在录制测试时智能地分析页面元素生成更具鲁棒性的选择器而不是脆弱的XPath或容易变化的CSS选择器。2.2 双引擎驱动录制生成与自然语言驱动Midscene.js的智能化主要体现在两个核心功能上这也是它能实现快速上手的关键。1. 智能录制与代码生成这是最直观的功能。你启动Midscene.js的录制模式然后在真实浏览器中操作你的网页。你的每一次点击、输入、滚动都会被记录下来。但与传统录制工具不同的是Midscene.js会实时分析被操作元素的视觉特征、语义属性如ARIA角色、在DOM中的结构以及周边文本综合生成一个“最佳”的选择器。这个选择器可能结合了># 新建一个目录 mkdir midscene-demo cd midscene-demo # 初始化npm项目 npm init -y # 安装Playwright及相关浏览器 npm init playwrightlatest # 在安装向导中选择TypeScript/JavaScript建议将测试目录设为tests其他默认即可。 # 安装Midscene.js核心包 npm install midscene验证Playwright安装安装完成后Playwright会下载所需的浏览器。运行以下命令确保基础环境正常npx playwright test --headed这会运行Playwright自带的示例测试并打开浏览器窗口。如果能看到浏览器自动执行测试并通过说明Playwright环境OK。3.2 配置GPT-4o API密钥Midscene.js的AI能力需要接入大模型。这里我们以OpenAI的GPT-4o为例。你需要一个OpenAI的API账号并获取密钥。获取API密钥访问OpenAI平台在API Keys页面创建新的密钥并妥善保存。设置环境变量为了安全不建议将API密钥硬编码在代码中。创建.env文件在项目根目录OPENAI_API_KEYsk-your-actual-api-key-here同时安装dotenv包来读取环境变量npm install dotenv创建Midscene配置文件在项目根目录创建midscene.config.js或midscene.config.ts如果你用TypeScript。// midscene.config.js require(dotenv).config(); // 加载环境变量 const { defineConfig } require(midscene); module.exports defineConfig({ ai: { provider: openai, // 从环境变量读取API密钥 apiKey: process.env.OPENAI_API_KEY, // 指定使用 gpt-4o 模型 model: gpt-4o, // 可选设置请求的基础URL如果你使用代理或特定网关 // baseURL: https://api.openai.com/v1 }, // 指定底层的浏览器自动化工具为 playwright runner: playwright, // Playwright的配置可以继承项目已有的playwright.config.js playwrightConfig: { // 例如设置全局超时或视口大小 timeout: 30000, viewport: { width: 1280, height: 720 } } });注意API成本与速率限制使用GPT-4o API是收费的并且有速率限制。在测试开发阶段尤其是页面DOM结构复杂时每次将页面信息发送给AI进行分析可能会消耗不少Token。建议初期在简单页面上试用并关注OpenAI账户的用量和费用。对于稳定的、需要大量运行的测试用例更经济的做法是使用智能录制生成基础脚本然后进行人工优化和固化而非每次都调用AI。3.3 编写你的第一个自然语言测试现在我们来创建一个测试文件用自然语言描述一个测试场景。创建测试文件在tests目录下创建example.spec.js。编写测试代码// tests/example.spec.js const { test, expect } require(playwright/test); // 引入Midscene的“描述”功能 const { describe } require(midscene/describe); test(使用Midscene.js和GPT-4o测试百度搜索, async ({ page }) { // 第一步导航到目标页面 await page.goto(https://www.baidu.com); // 第二步使用Midscene的describe函数用自然语言描述测试步骤 // 这个函数会调用配置的AI模型GPT-4o将描述转化为Playwright操作 await describe( page, 在搜索输入框里输入“Midscene.js”然后点击“百度一下”按钮进行搜索。, { testId: search-flow } // 可选给这个描述步骤一个ID便于调试 ); // 第三步等待搜索结果页面加载这里我们假设AI的操作会完成页面跳转 // 我们可以等待一个搜索结果特有的元素出现比如包含“百度为您找到”的文本 await expect(page.locator(text百度为您找到)).toBeVisible({ timeout: 10000 }); // 第四步继续用自然语言描述后续验证 await describe( page, 检查搜索结果中第一个结果的标题部分是否包含“Midscene”这个词。 ); // 注意AI执行的具体操作和定位的元素可能每次略有不同这是基于其当前对页面的理解。 // 对于需要严格断言的情况建议在AI执行后用传统的Playwright断言进行补充。 const firstResultTitle page.locator(#content_left .result h3 a).first(); await expect(firstResultTitle).toContainText(Midscene); });运行测试npx playwright test tests/example.spec.js --headed首次运行时Midscene.js会调用GPT-4o API。你会看到浏览器打开百度首页然后搜索框自动输入“Midscene.js”并点击搜索最后它会尝试检查第一个结果的标题。控制台会输出Midscene.js的解析和执行日志。实操心得首次运行可能较慢因为需要将页面信息简化后的DOM或可访问性树发送给GPT-4o并等待其返回操作序列所以第一次描述执行会比纯Playwright脚本慢。这属于正常现象。描述需要尽可能清晰“第一个结果”这种描述在AI看来可能有歧义。更精确的描述可以是“检查ID为‘content_left’的容器内第一个包含类名‘result’的元素中的标题链接文本”。但这样又失去了自然语言的优势。需要在“自然”和“精确”之间找到平衡。对于关键断言建议像示例中一样在describe之后用明确的Playwright选择器进行补充断言。注意页面状态确保在调用describe时页面已经处于稳定状态。如果页面还在加载或弹窗未关闭AI基于不完整的页面信息做出的决策可能会出错。4. 智能录制生成测试脚本详解对于大多数重复性的功能测试使用录制功能生成基础脚本再进行微调是最高效的方式。我们来详细走一遍这个流程。4.1 启动录制模式Midscene.js提供了命令行工具来启动录制会话。启动录制器在项目根目录下运行npx midscene record这会启动一个本地服务器并通常在浏览器中打开一个录制控制台页面同时会打开一个新的或重用现有的浏览器窗口用于操作。配置录制目标在控制台页面你需要输入你想要测试的网站起始URL例如http://localhost:5173你的本地开发服务器或一个线上地址。4.2 执行操作与脚本生成在浏览器中操作在打开的浏览器窗口里像真实用户一样操作你的应用。点击按钮、链接填写表单切换选项卡等。Midscene.js的录制器会在后台监听这些操作。观察代码生成录制控制台页面会实时地将你的操作转换为Playwright代码。你会发现它生成的代码并非简单的记录坐标或原始的page.click(‘.btn’)。它会尝试为每个操作的元素生成一个“智能定位器”。示例你点击了一个写着“提交”的按钮。它可能生成// 优先使用文本定位 await page.getByRole(button, { name: 提交 }).click(); // 或者结合了角色和文本 await page.locator(button:has-text(提交)).click();如果这个按钮有>await page.getByTestId(submit-btn).click();添加断言录制过程中你可以在控制台手动触发“添加断言”命令通常是一个按钮然后点击页面上你希望断言其状态如可见、包含文本、属性值等的元素。录制器会生成对应的expect断言语句。结束录制与导出操作完成后在录制控制台点击结束。你可以将生成的完整测试脚本复制到剪贴板或者直接保存为.spec.js文件到你的测试目录中。4.3 优化生成的脚本录制生成的脚本是一个完美的起点但直接使用可能不够健壮或不符合项目规范需要人工干预优化。审查并强化定位器添加明确的测试ID这是最佳实践。与你的前端开发团队约定为关键交互元素添加>// tests/auth.setup.js async function login(page, username, password) { await page.goto(/login); await page.getByTestId(username-input).fill(username); await page.getByTestId(password-input).fill(password); await page.getByTestId(login-submit-btn).click(); // 等待登录成功跳转到首页 await expect(page).toHaveURL(/dashboard); } module.exports { login };然后在你的测试文件中导入并使用它。增加必要的等待与断言录制可能不会在所有异步操作后都添加完美的等待。你需要手动添加以确保稳定性。使用Playwright的自动等待机制如expect(locator).toBeVisible()本身就是一个等待。// 在触发一个会弹出模态框的按钮点击后 await page.getByTestId(open-modal-btn).click(); // 增加一个对模态框出现的断言同时也起到了等待作用 await expect(page.getByTestId(modal-content)).toBeVisible();注意事项录制不是万能的对于动态内容、复杂拖拽、Canvas绘图等操作录制可能无法完美捕捉或生成有效代码。这些场景可能需要手动编写Playwright脚本。保持脚本简洁录制可能会记录一些不必要的操作比如误点击。导出后仔细清理脚本只保留核心测试步骤。与CI/CD集成优化后的脚本需要能在无头headless模式下稳定运行这是集成到持续集成流水线的前提。确保所有定位器在无头模式下也工作正常。5. 高级技巧混合模式与自定义定位策略在实际项目中纯自然语言测试或纯录制测试可能都无法满足所有需求。混合使用并自定义Midscene.js的行为才能发挥最大威力。5.1 混合编程模式将Midscene.js的describe函数与手写Playwright代码混合使用灵活性最高。test(混合模式测试购物车流程, async ({ page }) { // 1. 手动导航和登录稳定且通用的部分 await page.goto(https://example.com); await login(page, testuser, password123); // 使用自定义的登录函数 // 2. 使用AI描述一个相对复杂但UI稳定的添加商品操作 // 假设商品列表的布局经常微调但“加入购物车”按钮的文本语义不变 await describe( page, 在商品列表中找到名为“JavaScript高级程序设计”的图书点击它的“加入购物车”按钮。 ); // 3. 手动进行精确断言购物车数量、提示信息等 await expect(page.getByTestId(cart-count)).toHaveText(1); await expect(page.locator(.toast-success)).toContainText(已加入购物车); // 4. 继续用AI描述去购物车结算 await describe( page, 点击页面右上角的购物车图标进入购物车页面然后点击“去结算”按钮。 ); // 5. 手动填写收货地址等表单这些字段有固定的testid手动写更可靠 await page.getByTestId(address-form).fillForm({/* ... 地址数据 ... */}); await page.getByTestId(submit-order).click(); // 6. 最终用AI辅助验证订单成功页面 await describe( page, 验证当前页面显示了“订单提交成功”的提示信息。 ); });这种模式将AI用于UI可能变化或自然语言描述更直观的部分而将稳定、核心的业务逻辑断言和操作留给手动编写的可靠代码。5.2 自定义与扩展定位策略Midscene.js允许你注入自己的“元素定位器生成逻辑”。你可以编写插件告诉Midscene.js当它分析一个元素时优先使用哪些属性。例如你的项目使用vue-test-utils为所有交互元素添加了>// midscene.config.js const { defineConfig } require(midscene); module.exports defineConfig({ // ... 其他配置 ... locator: { // 自定义定位器优先级生成函数 generateLocator: async ({ element, page }) { // 1. 首选>// playwright.config.js module.exports { use: { headless: true, // CI环境默认无头 viewport: { width: 1280, height: 720 }, // 录制视频或截图便于失败时排查 screenshot: only-on-failure, video: retain-on-failure, }, };6.4 GPT-4o API调用超时或报错问题现象测试执行时卡住最后报错网络超时或API错误。处理办法检查网络连接确保运行测试的机器可以访问api.openai.com或你配置的baseURL。在公司内网可能需要配置代理。检查API密钥与额度确认.env文件中的OPENAI_API_KEY正确无误并且账户有足够的余额和可用额度。调整超时设置在midscene.config.js中可以增加AI请求的超时时间。ai: { provider: openai, apiKey: process.env.OPENAI_API_KEY, model: gpt-4o, timeout: 60000, // 设置为60秒 }简化请求负载Midscene.js发送给AI的页面信息可能很大。查看其文档看是否支持配置发送信息的详细程度如只发送可见元素、限制DOM深度等。简化页面信息可以减少Token消耗提高响应速度并降低费用。考虑降级模型或备用方案对于非关键或对响应时间要求不高的测试可以考虑使用gpt-3.5-turbo等更快、更便宜的模型。或者对于已经稳定的测试流程直接使用优化后的录制脚本而不必每次调用AI。我个人在实际项目中引入Midscene.js的体会是它最适合作为“测试脚本创作”阶段的加速器而不是“测试执行”阶段的完全自治者。它的价值在于快速生成测试脚本的初稿以及处理那些UI布局经常变动、但功能语义相对稳定的场景。将它与精心维护的测试工具函数如login、setupData和稳定的语义化定位器>