当前位置: 首页 > news >正文

Qwen3vl+Midscene+Playwright自动化AI测试框架搭建流程(win11本地安装)


一、环境准备

Windows 11(其他 Windows 版本同理)

安装Node.js:v20+(不建议使用18及以下版本)

安装npm(20及以后的node.js正常已经集成了npm)


二、项目初始化

在Windows系统中创建项目根目录,如:D:/Midscene,并初始化

新建Midscene根目录,如:D:/Midscene 进入Midscene根目录中,打开命令行执行初始化命令:npm init -y

三、安装核心依赖

在Midscene根目录中,打开命令行执行以下安装命令:

npm install @playwright/test @midscene/web playwright npm install node-autoit-koffi dotenv npx playwright install chromium

其中node-autoit-koffi插件用于操作弹框等特殊控件,需要同步下载客户端工具autoit-v3,解压到Midscene根目录下即可


四、配置 Playwright

创建playwright.config.js,在调试阶段建议把报告中的截图、视频和异常信息都设为开启,后续跑成熟了可以改为失败后展示

javascript

// playwright.config.js const { defineConfig } = require('@playwright/test'); module.exports = defineConfig({ testDir: './testcase', timeout: 1800000, // 超时时间默认为30分钟 retries: 0, use: { headless: false, launchOptions: { args: [ '--start-maximized', // 启动时最大化窗口 '--disable-features=ExternalProtocolDialog',// 关闭外部协议弹窗 '--disable-infobars', // 禁用“Chrome 正受到自动化测试软件控制”的提示栏 '--disable-notifications', // 禁用网页通知弹窗 '--disable-popup-blocking', // 禁用弹出窗口拦截 ], }, viewport: null, screenshot: 'always',// only-on-failure 仅在失败时截图 video: 'always',// retain-on-failure 仅在失败时保留视频 trace: 'on', // on-first-retry 仅在第一次重试时启用跟踪 }, reporter: [ ['html', { outputFolder: 'playwright-report' }], ['line'], ], });

五、配置环境变量(Qwen3-VL 模型)

在项目根目录创建.env文件(建议把MIDSCENE_MODEL_TEMPERATURE随机性调低,避免AI天马行空的乱思考):

# 阿里云线上模型地址 MIDSCENE_MODEL_BASE_URL="https://dashscope.aliyuncs.com/compatible-mode/v1" MIDSCENE_MODEL_API_KEY="sk-xxxxxxx" MIDSCENE_MODEL_NAME="qwen3-vl-plus" MIDSCENE_MODEL_FAMILY="qwen3-vl" MIDSCENE_USE_QWEN_VL=1 MIDSCENE_PLANNING_MAX_ATTEMPTS=1 # 调整模型参数(最大token数、降低随机性、重试间隔时间) MIDSCENE_MODEL_MAX_TOKENS=4096 MIDSCENE_MODEL_TEMPERATURE=0.1 MIDSCENE_MODEL_RETRY_INTERVAL=10000 # 测试地址信息 TEST_BASE_URL="https://www.abc.com/login" #登录地址 TEST_USERNAME=abcdef TEST_PASSWORD=Abc111

六、编写钉钉登录、日期处理等辅助工具类

Midscene根目录下创建utils文件夹,新建autoLoginDingtalk.jsdate.js两个工具类,其中autoLoginDingtalk.js用于弹框识别和操作

// utils/autoLoginDingtalk.js import * as autoit from 'node-autoit-koffi'; async function autoLoginDingtalk({ page, agent, data }) { await agent.aiAct('点击页面右侧登录区域中的【点击图标直接登录】文字,页面跳转到认证页'); await page.waitForTimeout(3000); await agent.aiAct('点击认证页中位于二维码右侧的用户头像'); await page.waitForTimeout(3000); await autoit.init(); // 等待【登录弹窗】出现(前端类匹配) const authCheckDialog = await autoit.winGetHandle("[CLASS:CUICef2ProjectWnd]"); if (!authCheckDialog) { throw new Error("未检测到钉钉登录弹窗"); } // 激活窗口,状态设为1(正常显示),并等待窗口稳定 await autoit.winSetStateByHandle(authCheckDialog, 5); await autoit.sleep(500); // 等待窗口稳定 const ctrlHandle = await autoit.controlGetHandle(authCheckDialog, "[CLASS:Chrome_RenderWidgetHostHWND; INSTANCE:1]"); console.log('✅ 获取到钉钉登录弹框句柄: '+ctrlHandle); await autoit.controlClickByHandle(authCheckDialog, ctrlHandle, "left", 1, 200, 320); await autoit.sleep(500); console.log('✅ 已点击【登录】按钮'); await page.waitForTimeout(6000); } module.exports = { autoLoginDingtalk };

注:需要通过之前下载的autoit-v3工具包中的Au3Info_x64.exe程序点击finder tool拖拽到需要探查的按钮上即可获取定位信息,用于winGetHandle、controlGetHandle、controlClickByHandle方法入参。


date.js用于生成日、周、月、年等格式信息

// utils/date.js function formatDate(date, format = 'YYYY-MM-DD') { const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, '0'); const day = String(date.getDate()).padStart(2, '0'); const week = String(getWeekNumber(date)).padStart(2, '0'); switch (format) { case 'YYYY-MM-DD': return `${year}-${month}-${day}`; case 'YYYY-WW': return `${year}-${week}`; case 'YYYY-MM': return `${year}-${month}`; case 'YYYY': return `${year}`; default: return `${year}-${month}-${day}`; } } function getWeekNumber(date = new Date()) { const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate())); const dayNum = d.getUTCDay() || 7; d.setUTCDate(d.getUTCDate() + 4 - dayNum); const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1)); return Math.ceil((((d - yearStart) / 86400000) + 1) / 7); } module.exports = { formatDate };

工具类可以根据需要自行修改或新增,并在测试用例文件中通过如下方式引入:

const{formatDate}=require('../utils/date.js');const{autoLoginDingtalk}=require('../utils/autoLoginDingtalk.js');

七、编写第一个测试用例

Midscene根目录下创建testcase文件夹,新建test001.spec.js,注意这里调用的是本地浏览器,进而可以比较真实的模拟本机上的浏览器的配置,如果确实需要调用playwright安装时自带的chromium,直接去掉下方代码注释中写明了用于本地 Chrome的配置项

// testcase/test001.spec.js import { test } from '@playwright/test'; import { chromium } from 'playwright'; import { PlaywrightAgent } from '@midscene/web/playwright'; import 'dotenv/config'; import path from 'path'; import { log } from 'console'; const { formatDate } = require('../utils/date.js'); const { autoLoginDingtalk } = require('../utils/autoLoginDingtalk.js'); const loginUsername = process.env.TEST_USERNAME; const loginPassword = process.env.TEST_PASSWORD; const baseURL = process.env.TEST_BASE_URL; const testDataList = [ // { // name: 'UI自动化-生产指标分析-集团-月度-对比分析', // OrgLevel: '集团', // dateType: '月', // analysisType: '对比分析', // }, { name: 'UI自动化-生产指标分析-集团-周度-对比分析', OrgLevel: '集团', dateType: '周', analysisType: '对比分析', } ]; test.describe('UI自动化-生产指标分析-001', () => { let agent; let context; // 保存 persistent context let page; // 保存手动创建的 page test.beforeEach(async () => { // 使用项目下的独立目录(确保没有被其他 Chrome 占用) const userDataDir = path.join(process.cwd(), 'chrome-profile-test'); // 2. 启动持久化上下文(直接使用本地安装的 Chrome) context = await chromium.launchPersistentContext(userDataDir, { headless: false, channel: 'chrome', // 关键:使用本地 Chrome,而不是 Playwright 自带的 Chromium args: [ '--start-maximized', // 启动时最大化窗口 '--disable-features=ExternalProtocolDialog', // 禁用外部协议弹窗 '--disable-infobars', '--disable-notifications', '--disable-external-protocol-handling', ], }); // 3. 获取或创建页面 page = context.pages()[0] || await context.newPage(); await page.goto(baseURL); // 4. 创建 Midscene Agent(传入手动创建的 page) agent = new PlaywrightAgent(page, { waitForNetworkIdleTimeout: 10000, cache: { strategy: 'read-write', id: 'pre-establish-cache' }, }); }); test.afterEach(async () => { if (agent) await agent.destroy(); if (context) await context.close(); // 关闭持久化上下文 }); testDataList.forEach((data) => { test(data.name, async () => { console.info(data.name + ' - 测试开始'); await autoLoginDingtalk({ page,agent, data }); await agent.aiAct('鼠标悬停在【XX】菜单上'); await page.waitForTimeout(2000); await agent.aiAct('在弹出的下拉菜单中点击【生产指标分析】'); await page.waitForTimeout(2000); await agent.aiAct('鼠标悬停在【XX】菜单下方的【XX】子菜单上'); await page.waitForTimeout(2000); await agent.aiAct('在弹出菜单中点击第1列的第3个菜单【XX】'); await page.waitForTimeout(5000); await agent.aiAssert('检查左侧架构树:【XX】单选框默认选中【XX】,当前架构为【XX】'); const day = formatDate(new Date(), 'YYYY-MM-DD'); const week = formatDate(new Date(), 'YYYY-WW'); const month = formatDate(new Date(), 'YYYY-MM'); log('当前日期:', day, '当前周:', week, '当前月:', month); await agent.aiAssert('检查右侧图表区域顶部控件,从左到右依次为:日周月选项卡(默认为日)、时间选择器(默认为'+ day +'的前一天)、下拉菜单1(默认为三天平均),下拉菜单2(默认为全部)、其他筛选'); log('顶部控件验证通过!'); await agent.aiAssert('检查第1幅图表:左上角标题为【XX】'); log('第1幅图表验证通过!'); switch (data.dateType) { case '日': await agent.aiAct('切换【日周月选项卡】的时间格式为日,并在右侧【时间选择器】上选择起止时间均为'+day+'日的前一天'); break; case '周': await agent.aiAct('切换【日周月选项卡】的时间格式为周,并在右侧【时间选择器】上选择起止时间均为'+week+'周的前一周'); break; case '月': await agent.aiAct('切换【日周月选项卡】的时间格式为月,并在右侧【时间选择器】上选择起止时间均为'+month+'月的前一月'); break; } console.info(data.name + ' - 测试通过!'); }); }); });

截止到这一步,项目整体目录和文件已经全部创建完成,结构如下图所示,框里的是主体文件


八、运行测试

在根目录下打开命令行执行

npx playwright test test001.spec.js

如果一切配置正确的话,命令行会输出部分日志如下:

Running 2 tests using 1 worker UI自动化-生产指标分析001 - 测试开始 ✅ 获取到钉钉登录弹框句柄: 1444844 ✅ 已点击【登录】按钮 当前日期: 2026-06-24 当前周: 2026-26 当前月: 2026-06 顶部控件验证通过! 第1幅图表验证通过! UI自动化-生产指标分析001 - 测试通过!

UI自动化-生产指标分析002 - 测试开始 ✅ 获取到钉钉登录弹框句柄: 6885268 ✅ 已点击【登录】按钮 当前日期: 2026-06-24 当前周: 2026-26 当前月: 2026-06 顶部控件验证通过! 第1幅图表验证通过! UI自动化-生产指标分析-002- 测试通过!

2 passed (4.5m)

To open last HTML report run:

npx playwright show-report


九、测试报告

测试报告在测试完成后由playwright自动生成并打开html格式文档


10、运行成本优化

1、Midscene 自带 AI 缓存,可在配置文件中开启

2、大模型云端添加 Prompt Caching

http://www.gsyq.cn/news/1628799.html

相关文章:

  • 2026淮北黄金回收白银回收铂金回收旧料回收怎么选?五家高实价铂金白银线下门店测评清单 + 联系方式
  • 用GPT-5.5重构遗留项目:一套可复用的迁移脚本分享(附避坑指南与教程)
  • GPT高效落地实战:日常工作与学习任务优化全流程方法论
  • LENA-R8与PIC18LF45K22的全球连接与低功耗定位方案
  • 好用的平衡机创新机构
  • HoRain云--Java多线程编程:6种实战技巧与避坑指南
  • 你用AI写分析文,总觉得像拼凑的?问题不在AI,在流程
  • 互联网大厂 Java 求职面试:从 Java SE 8 到微服务技术的深入探讨
  • VSCode集成GPT-5.5教程:如何选择插件与我的效率配置盘点清单
  • 基于51/STM32单片机空气质量监测系统/环境气体检测/WiFi传输/APP21(设计源文件+万字报告+讲解)(支持资料、图片参考_相关定制)_
  • 打破NVIDIA垄断:如何在非NVIDIA GPU上无缝运行CUDA程序的终极方案
  • Linux 系统编程 08:System V IPC
  • WandEnhancer开源增强工具:解锁游戏修改新体验的完整指南
  • QuickLookVideo:彻底解决Mac视频预览难题的高效实用解决方案
  • 汽车电子智能散热方案:DRV8213与PIC18F87J10温控设计
  • 【第三部分:线性回归(Linear Regression)】
  • 为什么开发团队远程访问代码仓库,不建议直接开放整个内网?
  • 终极指南:如何快速部署基于.NET Core的YiShaAdmin权限管理系统
  • 看板视图背后的流程驱动:任务卡片状态流转的触发机制设计
  • 基于STM32单片机车载儿童防窒息 车载儿童滞留检测安全座椅系统1(设计源文件+万字报告+讲解)(支持资料、图片参考_相关定制)_
  • 你知道C语言之父和C语言教父分别是谁吗?
  • 提示工程for程序员: 写出让AI理解的完美Prompt
  • SpringBoot使用maven打包提示jar中没有主清单属性
  • 无人机视角航拍森林树木健康状况检测数据集VOC+YOLO格式276张4类别
  • Vue 从零配置与完整使用教程(零基础保姆级)
  • 企业级 Claude Code 的统一记忆层,如何部署组织级 CLAUDE.md
  • 三节串联锂电池充电管理芯片IC完整资料包,5套方案原理图BOM打包带走
  • 2026年7月球场围网厂家推荐甄选指南,立足实体生产深耕体育场地防护工程
  • FOLDED LIGHT LINE代表什么意义
  • OAuth2 + JWT 企业单点登录(SSO)实战:多系统一次登录全打通(SpringBoot)