playwright学习方案
# Playwright 前端自动化测试框架 - 详细设计方案## 一、目标与核心流程一个**通用**的前端自动化测试框架核心链路为Markdown 写用例自然语言描述操作步骤→ 框架解析并自动执行→ 产出HTML 测试报告含截图 可复跑的回归 Python 脚本### 需求对照| # | 需求 | 方案 ||---|------|------|| 1 | 自然语言操作浏览器 | Markdown 中用自然语言描述步骤框架自动解析执行 || 2 | 执行完形成回归脚本 | 自动生成独立 .py 脚本包含精确选择器直接可跑 || 3 | 通用换任何 Web 系统都能用 | 系统相关配置抽到 profile.yaml框架不耦合任何业务 || 4 | 全自动只提供用例 | 一行命令 python main.py xxx.mdheadless 全自动 || 5 | 报告包含截图 | 每步自动截图HTML 报告可点击查看 || 6 | 报错记录后继续下一条 | 用例级 try/catch失败截图 记录自动跳到下一条 |---## 二、项目目录结构项目根目录/├── framework/ # 框架核心与业务无关│ ├── __init__.py # 包初始化导出各模块│ ├── engine.py # 执行引擎调度用例、隔离错误、继续下一条│ ├── parser.py # Markdown 解析器把 .md 用例解析成结构化数据│ ├── interpreter.py # 指令解释器自然语言 → Playwright 操作│ ├── locator.py # 智能定位8 级选择器降级策略│ ├── codegen.py # 代码生成自动生成回归 Python 脚本│ └── reporter.py # 报告生成HTML 报告 截图汇总│├── profiles/ # 系统配置不同 Web 系统配不同的 yaml│ └── default.yaml # 通用默认配置│├── test_cases/ # 测试用例Markdown 文件│ └── (你写的 .md 文件放这里)│├── outputs/ # 执行产物每次运行自动创建时间戳子目录│ ├── reports/ # HTML 报告│ ├── screenshots/ # 截图│ ├── videos/ # 操作视频│ └── scripts/ # 生成的回归脚本│├── main.py # 入口python main.py test_cases/你的用例.md└── config.yaml # 全局配置---## 三、Markdown 用例格式用户只需按以下格式写 Markdownmarkdown# 测试计划: XXX系统## 系统信息| 配置项 | 值 ||--------|-----|| 目标地址 | https://your-system.com || 登录地址 | https://your-system.com/login || 账号 | admin || 密码 | 123456 || 浏览器 | chromium || 分辨率 | 1920x1080 |---## 用例1: 登录功能正常流程**优先级**: P0**标签**: 登录, 冒烟测试1. 打开登录页面2. 输入账号 admin 和密码 1234563. 点击登录按钮4. 验证页面包含欢迎或首页5. 截图---## 用例2: 菜单导航检查**优先级**: P11. 点击菜单用户管理2. 验证页面显示用户列表3. 截图4. 点击菜单系统设置5. 验证页面显示设置选项6. 截图---## 用例3: 异常场景测试**优先级**: P21. 打开登录页面2. 输入账号 admin 和密码 错误密码3. 点击登录按钮4. 验证页面出现错误提示5. 截图### 格式规范- 以 ## 用例 开头的行为用例标题- **优先级** 和 **标签** 为可选元数据- 以 1. 2. 编号开头的行为操作步骤- 提取反引号内的参数值如 admin - 验证页面包含欢迎或首页 → 多个候选文本匹配任一即可---## 四、各模块详细设计### 4.1 parser.py —— Markdown 解析器**输入**Markdown 文件路径**输出**TestPlan 对象包含 system_info 字典和 test_cases 列表#### 解析流程Markdown 文件│├─→ 解析 ## 系统信息 表格 → dict {target_url, login_url, username, ...}│├─→ 按 ## 用例 分割块│ └─→ 每个块解析为 TestCase 对象│ ├─→ 提取优先级、标签│ └─→ 提取步骤文本 → 正则匹配 18 种模式│└─→ 输出 TestPlan.to_dict()#### 18 种自然语言模式映射表| 自然语言模式 | 解析为 action | 提取参数 ||-------------|-------------|---------|| 打开登录页面 | navigate_login | — || 打开 XXX 页面 | navigate_page | pageX || 跳转到 XXX | navigate_page | pageX || 访问 XXX | navigate_url | urlX || 输入账号 \X\ 和密码 \Y\ | fill_credentials | username, password || 输入 X 到「Y」 | fill_by_label | value, label || 在「X」中输入 Y | fill_by_label | label, value || 点击 X 按钮 | click_button | targetX || 点击菜单「X」 | click_menu | targetX || 点击链接「X」 | click_link | targetX || 点击 X | click | targetX || 验证页面包含「X」或「Y」 | assert_text | texts[X,Y] || 验证 URL 包含 X | assert_url | url_fragmentX || 验证 X 可见 | assert_visible | targetX || 验证 X 不存在 | assert_not_visible | targetX || 等待 N 秒 | wait | secondsN || 截图 / 截屏 | screenshot | — || 选择「X」中的「Y」 | select_option | target, option || 勾选 X | check | targetX || 取消勾选 X | uncheck | targetX || 滚动到 X | scroll_to | targetX || 悬浮到 X | hover | targetX |**设计决策为什么用正则而不是 NLP/LLM**测试用例的步骤描述本身就是结构化意图不是随意对话。点击登录按钮在不同人写的用例里高度一致正则足够。LLM 有不确定性——同一个句子两次解析可能不同——测试需要可复现。18 种模式覆盖 90% 常见操作未匹配的标记为 unknown 但不中断。---### 4.2 interpreter.py —— 指令解释器**输入**单条解析后的 step 字典{action, params...}**输出**调用 Playwright API 执行操作成功返回 True失败抛异常#### 动态分发机制pythonclass InstructionInterpreter:def execute(self, step):action step.get(action)handler getattr(self, f_handle_{action}, None)handler(step) # 动态调用 _handle_click / _handle_fill / ...每个操作类型对应一个 _handle_xxx 方法新增操作只需加方法不改调用逻辑。#### 自然语言 → Playwright API 映射| 操作类型 | Playwright API | 说明 ||---------|---------------|------|| navigate_login | page.goto(login_url) | 从系统信息取 login_url || navigate_page | page.goto(url) | 拼接 target_url page_name || navigate_url | page.goto(url) | 支持绝对/相对路径 || fill_credentials | locator.find_input_field(type) | 自动检测用户名/密码框 || fill_by_label | locator.find_by_label(label) | 按 label 文本定位 || click | locator.smart_find(target).click() | 8 级选择器策略 || click_button | smart_find(target, prefer_rolebutton) | 优先按钮角色 || click_menu | smart_find(target, prefer_rolenavigation) | 优先导航区域 || click_link | smart_find(target, prefer_rolelink) | 优先链接角色 || assert_text | wait_for_selector(textX) | 多候选文本匹配任一 || assert_url | assert fragment in page.url | URL 片段断言 || assert_visible | smart_find(target).is_visible() | 可见性断言 || wait | page.wait_for_timeout(N*1000) | 显式等待 || screenshot | 由 engine 层处理 | 引擎统一截图管理 |#### 登录模块login() 方法自动检测登录表单遍历 15 种选择器- **用户名框**input[nameusername] → input[nameaccount] → input[nameemail] → input[typetext] → placeholder*用户名 → ...- **密码框**input[namepassword] → input[namepasswd] → input[typepassword] → placeholder*密码 → ...- **提交按钮**button[typesubmit] → button:has-text(登录) → button:has-text(Sign in) → .login-btn → ...---### 4.3 locator.py —— 8 级智能定位策略#### 选择器优先级从快到慢、从精确到模糊Level 1: getByRole(button, {name: xxx}) ← 最优先语义化角色Level 2: getByLabel(xxx) ← form 标签关联Level 3: getByPlaceholder(xxx) ← placeholder 匹配Level 4: getByText(xxx) ← 精确文本 → 模糊文本Level 5: page.locator(#xxx) ← ID 选择器Level 6: page.locator([data-testidxxx]) ← 测试专用属性Level 7: page.locator(css: ...) ← CSS 选择器Level 8: page.locator(xpath: //*[...]) ← XPath 兜底#### 设计哲学稳定性决定优先级| 层级 | 稳定性 | 原因 ||------|--------|------|| L1-L4 | **高** | 不依赖 DOM 结构。CSS 类名改了、布局重排、div 变 span——这些 API 不受影响 || L5-L6 | **中** | 依赖开发者主动维护。id 可能冲突data-testid 不一定有 || L7-L8 | **低** | CSS/XPath 最脆弱前端改下 DOM 嵌套就可能失效仅做最后兜底 |#### 降级逻辑pythondef smart_find(target, prefer_roleNone):strategies _build_strategies(target) # 从 L1 到 L8 的策略列表for strategy in strategies:try:locator strategy(page)if locator and locator.is_visible():return locatorexcept:continue # 当前层失败静默降级到下一层return None # 8 层全失败 → 步骤标记 FAIL每一层尝试失败时静默吞掉异常全部失败才返回 None。保证大部分场景能找到元素同时不因某个选择器报错而中断流程。---### 4.4 engine.py —— 核心执行引擎#### 执行流程1. parser.parse_file(md_path) → 解构 TestPlan2. _setup_output_dirs() → 创建时间戳子目录3. sync_playwright() → 启动 browser context page4. interpreter.login() → 全局登录如果配置了账号5. for case in cases: → 逐条执行├─→ try: 执行所有步骤│ ├─→ 每步成功后截图│ └─→ 每步失败后截图 记录└─→ except: 用例级崩溃 → 截图 goto 恢复 continue6. reporter.generate() codegen.generate() → 生成报告和脚本7. _print_summary() → 打印汇总统计#### 两层错误隔离pythonfor case in cases:try: # 用例级 tryfor step in case.steps:try: # 步骤级 tryinterpreter.execute(step)step_result.status passscreenshot() # 成功截图except Exception:step_result.status failscreenshot(prefixFAIL) # 失败截图continue # 继续同用例的下一步except Exception:case_result.status failscreenshot(prefixERROR)page.goto(target_url) # 恢复页面状态continue # 继续下一条用例**关键设计**- **步骤失败**该用例内其他步骤照常执行一个页面 5 个按钮第 2 个失败第 3-5 个还试- **用例崩溃**该用例标记 FAILpage 恢复到目标 URL下条用例从干净状态开始#### StepResult 和 CaseResultpythonclass StepResult:index: int # 步骤序号action: dict # 原始操作status: str # pass | failerror: str # 失败时的错误信息screenshot_path: str # 截图路径class CaseResult:case: TestCase # 原始用例status: str # pass | failstep_results: list[StepResult]error: strstart_time / end_timepassed_steps / failed_steps / total_steps---### 4.5 reporter.py —— HTML 报告生成#### 报告结构┌─────────────────────────────────────┐│ 测试计划名称 生成时间 │├─────────────────────────────────────┤│ [总用例数] [通过] [失败] [步骤] [通过率] │ ← 6 个统计卡片├─────────────────────────────────────┤│ ████████████████░░ 81.3% │ ← 通过率进度条├─────────────────────────────────────┤│ ✓ TC001 登录功能正常流程 P0 5/5 │ ← 可折叠用例卡片│ ┌─ 步骤表格 ────────────────────┐ ││ │ #│状态│描述 │操作│截图 │ ││ │ 1│ ✓ │打开登录页面│goto│[截图] │ ││ │ 2│ ✓ │输入账号密码│fill│[截图] │ ││ │ ... │ ││ └────────────────────────────────┘ ││ ✗ TC003 异常场景测试 P2 2/5 ││ ┌─ 步骤表格失败项红色高亮────┐ ││ │ 4│ ✗ │验证错误提示 │assert│[截图]│││ │ 错误: 未找到预期文本 │ ││ └────────────────────────────────┘ │└─────────────────────────────────────┘#### 交互功能- **折叠卡片**默认收起点击用例头展开步骤详情- **截图缩略图**200px 宽显示点击放大到原始尺寸再次点击恢复- **失败高亮**失败步骤红色背景附带错误信息- **优先级标签**P0 红色 / P1 橙色 / P2 绿色---### 4.6 codegen.py —— 回归脚本生成#### 设计原则生成的脚本- **独立可运行**不依赖框架任何模块pip install playwright 就能跑- **带异常处理**每个操作用 try/catch 包裹失败打印错误 截图- **可脱离 Markdown**回归脚本不需要原始用例文件可以直接发给别人#### 生成示例python# 自动生成的回归测试脚本# 生成时间: 2026-06-24 22:00:00# 测试计划: 通用 Web 系统from playwright.sync_api import sync_playwrightdef run():with sync_playwright() as p:browser p.chromium.launch(headlessTrue)page browser.new_page(viewport{width: 1920, height: 1080})# ---- [TC001] 登录功能正常流程 ----try:page.goto(https://example.com/login, wait_untilnetworkidle)page.locator(input[nameusername]).first.fill(admin)page.locator(input[typepassword]).first.fill(123456)page.locator(button[typesubmit]).first.click()page.wait_for_selector(text欢迎, timeout5000)page.screenshot(pathTC001_step5.png, full_pageTrue)except Exception as e:print(f[FAIL] TC001 登录功能正常流程: {e})page.screenshot(pathFAIL_TC001.png, full_pageTrue)# ---- [TC002] 菜单导航检查 ----try:page.get_by_text(用户管理).first.click(timeout3000)page.wait_for_selector(text用户列表, timeout5000)# ...except Exception as e:print(f[FAIL] TC002 菜单导航检查: {e})browser.close()if __name__ __main__:run()---### 4.7 profiles/default.yaml —— 通用性保障框架本身不耦合任何业务系统。所有系统差异通过 profile 管理yaml# 所有配置项默认 auto框架优先自动检测检测不到再用配置值系统名称: 登录方式: auto # auto: 自动检测 | none: 无需登录 | form: 表单登录登录页识别: auto # 自动通过URL模式识别/login, /signin 等用户名框定位: auto # auto: 自动检测 | #username: 手动指定密码框定位: auto登录按钮定位: auto登录后确认: auto # url_change: URL变化算成功 | text: 出现特定文本菜单位置: auto # sidebar: 左侧栏 | top: 顶部栏 | auto: 自动检测框架类型: auto # element-ui | ant-design | bootstrap | auto页面加载策略: networkidle截图方式: fullpage#### 自动检测逻辑- **登录页识别**检查 URL 是否包含 /login、/signin、/sign_in、/console 等模式- **表单检测**遍历 15 种选择器组合尝试用户名框和密码框- **菜单位置**检测 sidebar 和 top nav 区域按布局特征判断换一个 Web 系统理论上只需要改 Markdown 用例里的系统信息表格。---### 4.8 config.yaml —— 全局配置yamlbrowser: chromium # chromium | firefox | webkitviewport:width: 1920height: 1080mode: headless # headless | headedtimeout:default: 30000 # 默认超时毫秒navigation: 60000 # 导航超时action: 10000 # 操作超时screenshot:format: pngfull_page: truereport:title: 自动化测试报告output_dir: outputs # 输出根目录concurrency: 1 # 并发数---## 五、使用方式### 环境准备bashpip install playwright pyyamlpython -m playwright install chromium### 运行测试bash# 基本用法python main.py test_cases/demo_test.md# 指定输出目录python main.py test_cases/demo_test.md --output my_outputs# 指定配置文件python main.py test_cases/demo_test.md --config my_config.yaml### 执行效果Playwright Auto Test Framework测试计划: 通用 Web 系统用例总数: 3 | 浏览器: Chromium (headless)[PASS] TC001 登录功能正常流程 (5/5 通过)[PASS] TC002 菜单导航检查 (6/6 通过)[FAIL] TC003 异常场景测试 (2/5 通过) → 已截图记录继续执行总步骤: 16 | 通过: 13 | 失败: 3 | 通过率: 81.3%--------------------------------------------------报告: outputs/20260624_220000/reports/report.html脚本: outputs/20260624_220000/scripts/regression_20260624.py截图: outputs/20260624_220000/screenshots/---## 六、文件清单| 文件 | 行数 | 说明 ||------|------|------|| framework/__init__.py | ~20 | 包初始化导出所有模块 || framework/parser.py | ~250 | Markdown 解析器18 种自然语言模式 || framework/interpreter.py | ~280 | 指令解释器NL → Playwright API || framework/locator.py | ~180 | 8 级选择器降级策略 || framework/engine.py | ~310 | 核心执行引擎两层错误隔离 || framework/reporter.py | ~220 | HTML 报告生成含交互式 UI || framework/codegen.py | ~125 | 回归脚本代码生成 || profiles/default.yaml | ~15 | 通用默认配置 || main.py | ~70 | 命令行入口 || config.yaml | ~25 | 全局配置 || test_cases/demo_test.md | ~40 | 通用示例用例 |**总计约 1500 行 Python 代码**---## 七、扩展方向### 短期可扩展- 支持更多自然语言模式如拖拽 A 到 B、双击、右键菜单- 支持数据驱动同一个用例用多组数据执行- 支持用例间变量传递### 中期可扩展- 并发执行多个用例- 集成 CI/CDJenkins/GitHub Actions 插件- 对比截图 视觉回归检测- 邮件/钉钉/企微通知### 长期可扩展- LLM 辅助定位当 8 级降级全失败时用 LLM 理解页面结构并生成选择器- 录制回放浏览器操作录制 → 自动生成 Markdown 用例- 性能指标采集页面加载时间、API 响应时间