关于 webapp-testing
用于编写 Python Playwright 脚本,在浏览器中测试本地 Web 应用。它指导端到端与界面测试,包括表单验证、截图和浏览器日志采集,并提供选择器与等待策略。
主要功能
- 01编写原生 Python Playwright 脚本
- 02支持端到端与界面测试
- 03测试表单及其验证
- 04采集截图与浏览器日志
- 05指导选择器和等待策略
适用场景
- 01测试本地 Web 应用的用户流程
- 02检查表单提交与验证
- 03截取截图以进行视觉核验
- 04排查浏览器错误与测试失败
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ curiositech/ some_claude_skills --skill webapp-testing安装后在 Claude Code 中输入/webapp-testing确认已加载。
附带文件.claude-plugin/plugin.json
详细介绍
Web 应用测试
编写原生 Python Playwright 脚本来测试本地 Web 应用。
适用场景
✅ 适用于:
- Web 应用的端到端测试
- UI 自动化和交互测试
- 视觉回归测试
- 浏览器日志捕获和调试
- 截图验证
- 表单提交和验证测试
❌ 不适用于:
- 不使用浏览器的纯 API 测试(使用 requests/httpx)
- 单个函数的单元测试
- 移动应用测试(使用 Appium)
- 负载/性能测试(使用 k6/Locust)
决策树:选择合适的方法
User task → Is it static HTML?
├─ Yes → Read HTML file directly to identify selectors
│ ├─ Success → Write Playwright script using selectors
│ └─ Fails/Incomplete → Treat as dynamic (below)
│
└─ No (dynamic webapp) → Is the server already running?
├─ No → Start server first, then run Playwright
│
└─ Yes → Reconnaissance-then-action:
1. Navigate and wait for networkidle
2. Take screenshot or inspect DOM
3. Identify selectors from rendered state
4. Execute actions with discovered selectors
Playwright 核心模式
基本测试结构
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(headless=True) # Always headless
page = browser.new_page()
page.goto('http://localhost:5173')
page.wait_for_load_state('networkidle') # CRITICAL for SPAs
# ... your test logic
browser.close()
先侦察再操作模式
步骤 1:检查渲染后的 DOM
page.screenshot(path='/tmp/inspect.png', full_page=True)
content = page.content()
buttons = page.locator('button').all()
步骤 2:根据检查结果确定选择器
步骤 3:使用找到的选择器执行操作
选择器策略(优先级顺序)
-
基于角色(最利于无障碍访问):
page.get_by_role("button", name="Submit") page.get_by_role("textbox", name="Email") -
基于文本(易读,但容易因文案变更而失效):
page.get_by_text("Sign In") page.get_by_label("Password") -
测试 ID(稳定、明确):
page.get_by_test_id("login-button") -
CSS 选择器(最后手段):
page.locator(".btn-primary") page.locator("#submit-form")
常见反模式
反模式:未等待网络空闲
表现:测试在本地通过,在 CI 中失败;找不到元素
问题:现代 SPA 会在页面初次加载后动态加载内容
解决方案:
# ❌ Wrong
page.goto('http://localhost:3000')
page.click('button') # Element may not exist yet
# ✅ Correct
page.goto('http://localhost:3000')
page.wait_for_load_state('networkidle')
page.click('button')
反模式:硬编码等待
表现:测试中到处散布 time.sleep(3)
问题:速度慢、不可靠,也无法根据页面实际状态调整
解决方案:
# ❌ Wrong
time.sleep(5)
page.click('.dynamic-button')
# ✅ Correct
page.wait_for_selector('.dynamic-button', state='visible')
page.click('.dynamic-button')
反模式:JavaScript 执行前检查 DOM
表现:页面内容为空,静态分析中缺少元素
问题:在客户端渲染完成前读取 HTML
解决方案:对于动态应用,检查前务必等待 networkidle
等待策略
# Wait for element to appear
page.wait_for_selector('#my-element')
# Wait for element to be visible
page.wait_for_selector('#my-element', state='visible')
# Wait for element to be hidden
page.wait_for_selector('#my-element', state='hidden')
# Wait for navigation
page.wait_for_url('**/dashboard')
# Wait for network idle (all requests complete)
page.wait_for_load_state('networkidle')
# Custom wait with timeout
page.wait_for_function('document.querySelector(".loaded")')
截图模式
# Full page screenshot
page.screenshot(path='/tmp/full.png', full_page=True)
# Element screenshot
page.locator('#header').screenshot(path='/tmp/header.png')
# Before/after comparison
page.screenshot(path='/tmp/before.png')
# ... perform action ...
page.screenshot(path='/tmp/after.png')
控制台日志捕获
# Capture all console messages
messages = []
page.on('console', lambda msg: messages.append({
'type': msg.type,
'text': msg.text
}))
# Filter errors only
page.on('console', lambda msg:
print(f'ERROR: {msg.text}') if msg.type == 'error' else None
)
表单测试
# Fill form fields
page.fill('#email', '[email protected]')
page.fill('#password', 'secret123')
# Select dropdown
page.select_option('#country', 'US')
# Check checkbox
page.check('#terms')
# Submit form
page.click('button[type="submit"]')
# Verify submission
page.wait_for_url('**/success')
断言
from playwright.sync_api import expect
# Element assertions
expect(page.locator('#title')).to_have_text('Welcome')
expect(page.locator('#count')).to_have_text('5')
expect(page.locator('.error')).to_be_hidden()
expect(page.locator('#submit')).to_be_enabled()
# Page assertions
expect(page).to_have_url('http://localhost:3000/dashboard')
expect(page).to_have_title('My App')
多页面场景
# Handle popup windows
with page.expect_popup() as popup_info:
page.click('#open-popup')
popup = popup_info.value
popup.wait_for_load_state()
# Handle new tabs
with context.expect_page() as new_page_info:
page.click('a[target="_blank"]')
new_page = new_page_info.value
测试文件组织
tests/
├── conftest.py # Shared fixtures
├── test_login.py # Login flows
├── test_dashboard.py # Dashboard features
├── test_forms.py # Form submissions
└── screenshots/ # Visual artifacts
运行测试
# Run single test file
python -m pytest tests/test_login.py
# Run with browser visible (debugging)
PWDEBUG=1 python -m pytest tests/test_login.py
# Generate trace for debugging
python -m pytest --tracing=on tests/test_login.py
最佳实践
- 同步脚本使用
sync_playwright() - 完成后务必关闭浏览器
- 使用描述性选择器:优先选择角色、文本、测试 ID,而非 CSS
- 添加适当的等待:
wait_for_selector()、wait_for_load_state() - 失败时捕获截图,便于调试
- 保持测试相互独立——每个测试都应自行设置状态
此技能涵盖:Playwright 最佳实践 | 选择器策略 | 等待模式 | 反模式规避 | 端到端测试工作流
评论
支持 Markdown