mcpField
W
webapp-testing

webapp-testing

github.com/curiositech测试与质量257174 次安装1 天前更新
代码仓库

关于 webapp-testing

用于编写 Python Playwright 脚本,在浏览器中测试本地 Web 应用。它指导端到端与界面测试,包括表单验证、截图和浏览器日志采集,并提供选择器与等待策略。

主要功能

  1. 01编写原生 Python Playwright 脚本
  2. 02支持端到端与界面测试
  3. 03测试表单及其验证
  4. 04采集截图与浏览器日志
  5. 05指导选择器和等待策略

适用场景

  1. 01测试本地 Web 应用的用户流程
  2. 02检查表单提交与验证
  3. 03截取截图以进行视觉核验
  4. 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:使用找到的选择器执行操作

选择器策略(优先级顺序)

  1. 基于角色(最利于无障碍访问):

    page.get_by_role("button", name="Submit")
    page.get_by_role("textbox", name="Email")
    
  2. 基于文本(易读,但容易因文案变更而失效):

    page.get_by_text("Sign In")
    page.get_by_label("Password")
    
  3. 测试 ID(稳定、明确):

    page.get_by_test_id("login-button")
    
  4. 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

最佳实践

  1. 同步脚本使用 sync_playwright()
  2. 完成后务必关闭浏览器
  3. 使用描述性选择器:优先选择角色、文本、测试 ID,而非 CSS
  4. 添加适当的等待:wait_for_selector()、wait_for_load_state()
  5. 失败时捕获截图,便于调试
  6. 保持测试相互独立——每个测试都应自行设置状态

此技能涵盖:Playwright 最佳实践 | 选择器策略 | 等待模式 | 反模式规避 | 端到端测试工作流

评论

支持 Markdown