Midscene.js框架(4):测试框架集成
本篇将聚焦于测试框架集成——重点讲解如何将 Midscene 的 AI 自动化能力与 Python 生态中最流行的Pytest测试框架深度结合实现规范化的 UI 自动化测试。前言一、与 Pytest 深度集成1.1环境准备1.2配置模型1.3编写第一个 Pytest 测试二、Fixture 管理2.1 为什么需要 Fixture 管理2.2 设计 Midscene 专用 Fixture2.3 使用 Fixture 编写测试三、测试生命周期3.1 生命周期全景图3.2 准备阶段Setup3.3 执行阶段Test Body3.4 断言策略AI 断言 vs 传统断言3.5 清理阶段Teardown3.6 完整生命周期示例3.7 报告合并前言在前面的教程中我们已经掌握了两种使用 Midscene.js 的方式通过 JavaScript/TypeScript API 编程以及通过 YAML 脚本快速执行。这两种方式适合快速验证和独立自动化任务但在企业级测试场景中我们还需要解决一系列工程化问题环境管理浏览器启动/关闭、模型配置、测试数据准备用例隔离每条用例独立的浏览器上下文互不干扰生命周期控制用例执行前的初始化、执行后的清理与报告生成复用与共享登录状态、公共配置、Agent 实例的跨用例复用持续集成与 CI/CD 流水线无缝对接支持批量执行和结果收集Midscene.js 本身是 JavaScript/TypeScript 框架官方提供了与 Playwright Test Runner 的深度集成。但对于 Python 技术栈的团队来说Pytest才是最自然的选择。社区项目pymidscene提供了 Midscene.js 的 Python SDK完整移植了核心 API使得 Python 开发者也能享受 AI 驱动的 UI 自动化能力。Pytest 的优势在于一、与 Pytest 深度集成1.1环境准备首先安装 pymidscene 和 Playwright 浏览器驱动pip install pymidscene playwright install chromiumpymidscene 要求 Python ≥ 3.10且当前版本v0.3.0基于 Playwright 作为浏览器驱动层。1.2配置模型Midscene.js 的灵魂在于 AI 模型——所有元素的定位、操作的规划、断言判断都依赖大模型完成。在 pymidscene 中创建.env文件或直接设置环境变量export MIDSCENE_MODEL_NAMEdoubao-seed-1-6-251015 export MIDSCENE_MODEL_API_KEYyour-api-key export MIDSCENE_MODEL_BASE_URLhttps://ark.cn-beijing.volces.com/api/v3 export MIDSCENE_MODEL_FAMILYdoubao-vision1.3编写第一个 Pytest 测试pymidscene 的核心是 PlaywrightAgent 类它封装了所有 AI 驱动的交互能力import asyncio import pytest from playwright.async_api import async_playwright from pymidscene import PlaywrightAgent pytest.mark.asyncio async def test_search_flow(): 测试搜索功能的基本流程 async with async_playwright() as p: browser await p.chromium.launch(headlessTrue) page await browser.new_page() agent PlaywrightAgent(page) # 导航到目标页面 await page.goto(https://www.example.com) # 使用 AI 进行页面交互 await agent.ai_click(搜索输入框) await agent.ai_input(搜索输入框, Midscene AI testing) await agent.ai_click(搜索按钮) # AI 断言验证搜索结果已显示 await agent.ai_assert(页面上显示了搜索结果列表) # AI 查询提取结构化数据 result await agent.ai_query({ first_result_title: 第一个搜索结果的标题, total_results_text: 搜索结果数量的文本 }) print(f查询结果: {result}) # 生成报告 report_path agent.finish() print(f报告已保存至: {report_path}) await browser.close()这段代码演示了 Midscene.js 测试的核心模式创建 Agent将 Playwright 的 Page 对象注入 AgentAI 操作用自然语言描述目标AI 自动完成定位与执行AI 断言用自然语言描述预期条件AI 查询从页面提取结构化数据报告生成自动生成可视化 HTML 报告二、Fixture 管理2.1 为什么需要 Fixture 管理在上面的示例中浏览器、页面、Agent 的创建和销毁逻辑都写在测试函数内部。当测试用例增多时这种模式会带来严重的代码重复和维护问题。Pytest 的 Fixture 机制恰好能解决这个问题——将可复用的前置准备抽象为独立的 Fixture在需要的测试中按需注入。2.2 设计 Midscene 专用 Fixture我们在 conftest.py 中创建三个核心 Fixture# conftest.py import pytest import os from playwright.async_api import async_playwright from pymidscene import PlaywrightAgent # # 1. 全局配置模型环境变量 # pytest.fixture(scopesession, autouseTrue) def setup_model_config(): 会话级别一次配置所有测试复用 os.environ[MIDSCENE_MODEL_NAME] doubao-seed-1-6-251015 os.environ[MIDSCENE_MODEL_API_KEY] your-api-key os.environ[MIDSCENE_MODEL_BASE_URL] ( https://ark.cn-beijing.volces.com/api/v3 ) os.environ[MIDSCENE_MODEL_FAMILY] doubao-vision yield # session 结束后无需清理环境变量 # # 2. 浏览器 FixtureSession 级别 # pytest.fixture(scopesession) async def browser(): 会话级别整个测试会话共享一个浏览器实例 async with async_playwright() as p: browser await p.chromium.launch( headlessTrue, # CI 环境建议设为 True args[--no-sandbox] # Docker 环境必需 ) yield browser await browser.close() # # 3. Agent FixtureFunction 级别 # pytest.fixture async def midscene_agent(browser): 函数级别每个测试用例拥有独立的 Page 和 Agent context await browser.new_context( viewport{width: 1280, height: 720} ) page await context.new_page() agent PlaywrightAgent(page) yield agent # 测试用例在此执行 # Teardown生成报告并清理资源 agent.finish() await context.close()这个 Fixture 架构借鉴了 Midscene.js 中 PlaywrightAiFixture 的设计理念分层管理不同生命周期的资源┌─────────────────────────────────────┐│ Session 级别全局共享 ││ ┌─────────┐ ┌──────────────────┐ ││ │ Browser │ │ Model Config │ ││ └─────────┘ └──────────────────┘ ││ │ ││ Function 级别每用例独立 ││ ┌──────────────────┐ ││ │ Context → Page │ ││ │ → Agent │ ││ └──────────────────┘ │└─────────────────────────────────────┘2.3 使用 Fixture 编写测试有了上述 Fixture 后测试用例变得清晰简洁# test_search.py import pytest pytest.mark.asyncio async def test_search_with_ai(midscene_agent): 使用 Fixture 注入 Agent不再需要手动管理浏览器生命周期 agent midscene_agent page agent.page # 可以通过 agent 获取底层 page 对象 await page.goto(https://www.example.com/search) # 直接用 AI 操作 await agent.ai_input(搜索框, AI testing framework) await agent.ai_click(搜索按钮) # 等待结果加载 await agent.ai_wait_for(搜索结果列表出现, timeout10000) # AI 断言 await agent.ai_assert(搜索结果数量大于 0) # 提取数据 items await agent.ai_query({ results: [ {title: 结果标题, link_text: 链接文字} ] }) assert len(items.get(results, [])) 0 pytest.mark.asyncio async def test_filter_by_category(midscene_agent): 另一个测试用例自动获得独立的 Page 和 Agent agent midscene_agent page agent.page await page.goto(https://www.example.com/products) await agent.ai_click(分类筛选器) await agent.ai_click(电子产品分类) await agent.ai_assert(页面只显示电子产品)三、测试生命周期理解 Midscene.js 测试的完整生命周期是编写稳定可靠测试的关键。整个生命周期可以分为四个阶段准备 → 执行 → 断言 → 清理。3.1 生命周期全景图┌──────────────────────────────────────────────────────────┐ │ Pytest 测试生命周期 │ ├───────────┬──────────────────────────────────────────────┤ │ session │ setup_model_config() │ │ start │ browser() launch │ ├───────────┼──────────────────────────────────────────────┤ │ function │ midscene_agent() │ │ setup │ ├── browser.new_context() │ │ │ ├── context.new_page() │ │ │ └── PlaywrightAgent(page) │ ├───────────┼──────────────────────────────────────────────┤ │ test │ ├── page.goto(url) ← 导航 │ │ body │ ├── agent.ai_click() ← AI 交互 │ │ │ ├── agent.ai_input() ← AI 输入 │ │ │ ├── agent.ai_wait_for() ← 等待条件 │ │ │ ├── agent.ai_query() ← 数据提取 │ │ │ └── agent.ai_assert() ← AI 断言 │ ├───────────┼──────────────────────────────────────────────┤ │ function │ midscene_agent() teardown │ │ teardown │ ├── agent.finish() ← 生成报告 │ │ │ └── context.close() ← 回收资源 │ ├───────────┼──────────────────────────────────────────────┤ │ session │ browser.close() │ │ end │ │ └───────────┴──────────────────────────────────────────────┘3.2 准备阶段Setup准备阶段负责初始化所有测试所需的环境和资源# conftest.py 中的 setup 逻辑 pytest.fixture(scopesession, autouseTrue) def setup_model_config(): Step 1: 配置 AI 模型最早执行 os.environ[MIDSCENE_MODEL_NAME] ... os.environ[MIDSCENE_MODEL_API_KEY] ... # ... pytest.fixture(scopesession) async def browser(): Step 2: 启动浏览器会话级别复用 async with async_playwright() as p: browser await p.chromium.launch(headlessTrue) yield browser # 所有测试结束后才关闭浏览器 pytest.fixture async def midscene_agent(browser): Step 3: 创建 Agent每个测试独立 context await browser.new_context( viewport{width: 1280, height: 720} ) page await context.new_page() agent PlaywrightAgent(page) yield agent # ...3.3 执行阶段Test Body执行阶段是测试的核心通过 AI 方法驱动浏览器完成业务操作pytest.mark.asyncio async def test_complete_checkout(midscene_agent): agent midscene_agent page agent.page # ---- 1. 导航 ---- await page.goto(https://shop.example.com) await page.wait_for_load_state(networkidle) # ---- 2. 浏览商品 ---- await agent.ai_click(「手机」分类) await agent.ai_wait_for(商品列表加载完成, timeout8000) # ---- 3. 加入购物车 ---- await agent.ai_click(第一个商品的「加入购物车」按钮) await agent.ai_assert(购物车数量显示为 1) # ---- 4. 进入结算 ---- await agent.ai_click(购物车图标) await agent.ai_click(「去结算」按钮) # ---- 5. 填写收货信息 ---- await agent.ai_input(收货人姓名输入框, 张三) await agent.ai_input(手机号码输入框, 13800138000) await agent.ai_input(详细地址输入框, 北京市朝阳区xxx路xxx号) # ---- 6. 提交订单 ---- await agent.ai_click(「提交订单」按钮) await agent.ai_wait_for(订单提交成功提示出现, timeout10000) # ---- 7. 最终断言 ---- await agent.ai_assert(页面显示「订单提交成功」)3.4 断言策略AI 断言 vs 传统断言Midscene.js 提供了两种断言方式各有适用场景最佳实践结合使用两者。先用 ai_query 提取结构化数据再用 Pytest 标准断言验证可以减少 AI 推理成本并提高稳定性pytest.mark.asyncio async def test_hybrid_assertion(midscene_agent): agent midscene_agent # ✅ 推荐传统断言验证数据 product await agent.ai_query({ name: 商品名称, price: 商品价格纯数字如 99.00, in_stock: 是否显示「有货」true/false }) assert product[in_stock] is True assert product[price] 0 assert len(product[name]) 0 # ✅ AI 断言验证视觉条件 await agent.ai_assert(商品图片已正确加载显示)3.5 清理阶段TeardownTeardown 阶段负责报告生成和资源回收。在 Fixture 的 yield 之后执行pytest.fixture async def midscene_agent(browser): context await browser.new_context() page await context.new_page() agent PlaywrightAgent(page) yield agent # Teardown 逻辑 # 1. 生成 HTML 报告 report_path agent.finish() print(f 测试报告: {report_path}) # 2. 关闭 context回收所有 Page await context.close()agent.finish() 方法会生成一个包含操作截图、AI 推理过程、耗时统计的 HTML 报告文件存放在 midscene_run/report/ 目录下。3.6 完整生命周期示例以下是一个完整的测试文件展示了生命周期的实际运作# test_lifecycle_demo.py import pytest from datetime import datetime pytest.fixture(scopemodule, autouseTrue) def module_setup(): Module 级别的 setup print(f\n [Module Setup] 测试模块开始于 {datetime.now()}) yield print(f\n [Module Teardown] 测试模块结束于 {datetime.now()}) pytest.fixture(autouseTrue) def function_setup(): Function 级别的 setup每个用例都执行 print(\n [Function Setup] 准备测试用例) yield print( [Function Teardown] 测试用例结束) pytest.mark.asyncio async def test_case_1(midscene_agent): 测试用例 1 print( 执行测试用例 1) agent midscene_agent await agent.page.goto(https://www.example.com) await agent.ai_assert(页面已成功加载) pytest.mark.asyncio async def test_case_2(midscene_agent): 测试用例 2 print( 执行测试用例 2) agent midscene_agent await agent.page.goto(https://www.example.com/about) await agent.ai_assert(页面包含关于我们的信息)执行 pytest -v -s test_lifecycle_demo.py输出将清晰展示各阶段的执行顺序。3.7 报告合并当测试用例较多时可以将多个 Agent 的报告合并为一个文件。在 pymidscene 中你可以通过自定义 Fixture 实现报告收集# conftest.py import pytest pytest.fixture(scopesession) def report_collector(): Session 级别的报告收集器 reports [] yield reports # 所有测试结束后合并报告 if reports: print(f\n 共生成 {len(reports)} 份报告) for r in reports: print(f - {r}) pytest.fixture async def midscene_agent(browser, report_collector): context await browser.new_context() page await context.new_page() agent PlaywrightAgent(page) yield agent report_path agent.finish() report_collector.append(report_path) await context.close()总结测试框架的集成是绕不开的关键环节。一个好的测试框架集成不仅要让测试用例编写高效还要提供灵活的 Fixture 管理机制和清晰的生命周期控制。参考资料Midscene.js 官方文档Midscene - Vision-Driven UI Automationpymidscene PyPIpymidscene · PyPIMidscene.js Playwright 集成指南Integrate with Playwright - Midscene - Vision-Driven UI AutomationPytest Fixture 官方文档About fixtures - pytest documentation