核心命令
wait_for_load_state(‘networkidle’)
webapp-testing 是使用 Playwright 进行 Web 应用测试的技能。它的核心是:自动化测试本地 Web 应用,验证前端功能,调试 UI 行为。
作为开发者,你可能遇到过:
webapp-testing 就是解决这些的。
手动测试 vs 自动化测试:
手动:- 耗时- 易出错- 重复性工作
自动化:- 快速- 可靠- 可重复测试的价值:
- 开发时发现 bug- 减少回归问题- 提高代码质量- 加快迭代速度用户任务 ↓是静态 HTML? ├─ 是 → 直接读取 HTML 找选择器 │ ├─ 成功 → 用选择器写 Playwright 脚本 │ └─ 失败 → 当作动态处理 │ └─ 否(动态 Web 应用) 服务器已运行? ├─ 否 → 用 with_server.py 启动服务器 │ 然后写简化 Playwright 脚本 │ └─ 是 → 侦察-行动模式: 1. 导航并等待 networkidle 2. 截图或检查 DOM 3. 从渲染结果识别选择器 4. 用发现的选择器执行操作python scripts/with_server.py \ --server "npm run dev" \ --port 5173 \ -- python your_automation.pypython scripts/with_server.py \ --server "cd backend && python server.py" --port 3000 \ --server "cd frontend && npm run dev" --port 5173 \ -- python your_automation.pyfrom playwright.sync_api import sync_playwright
with sync_playwright() as p: # 始终使用无头模式 browser = p.chromium.launch(headless=True) page = browser.new_page()
# 访问应用(服务器已运行并就绪) page.goto('http://localhost:5173')
# 关键:等待 JS 执行完成 page.wait_for_load_state('networkidle')
# ... 你的自动化逻辑
browser.close()# 截图page.screenshot(path='/tmp/inspect.png', full_page=True)
# 获取页面内容content = page.content()
# 获取所有按钮buttons = page.locator('button').all()从检查结果中选择器:
常见选择器:- CSS 选择器:#id, .class, tag- XPath://div[@class='xxx']- 文本选择器:page.get_by_text('登录')- 角色选择器:page.get_by_role('button')# 点击按钮page.locator('#submit-btn').click()
# 输入文字page.locator('input[name="email"]').fill('test@example.com')
# 等待元素page.wait_for_selector('.result')
# 断言assert page.locator('.success').is_visible()错误示例:page.goto('http://localhost:5173')content = page.content() # 太早了!
正确示例:page.goto('http://localhost:5173')page.wait_for_load_state('networkidle') # 等待 JS 执行content = page.content() # 现在 DOM 完整了错误:page.locator('#root > div > div:nth-child(3) > button')
更好:page.get_by_role('button', name='提交')page.get_by_text('登录')page.locator('.submit-btn').firstfrom playwright.sync_api import sync_playwright
def test_form_submission(): with sync_playwright() as p: browser = p.chromium.launch(headless=True) page = browser.new_page()
# 导航 page.goto('http://localhost:5173/form') page.wait_for_load_state('networkidle')
# 填写表单 page.get_by_label('用户名').fill('testuser') page.get_by_label('邮箱').fill('test@example.com') page.get_by_label('密码').fill('password123')
# 提交 page.get_by_role('button', name='提交').click()
# 验证成功 page.wait_for_selector('.success-message') assert '提交成功' in page.locator('.success-message').text_content()
browser.close()def test_navigation(): with sync_playwright() as p: browser = p.chromium.launch(headless=True) page = browser.new_page()
# 测试导航 page.goto('http://localhost:5173') page.wait_for_load_state('networkidle')
# 点击导航链接 page.get_by_role('link', name='关于').click()
# 验证页面跳转 assert '关于' in page.title()
browser.close()def test_screenshot(): with sync_playwright() as p: browser = p.chromium.launch(headless=True) page = browser.new_page()
page.goto('http://localhost:5173') page.wait_for_load_state('networkidle')
# 截图 page.screenshot(path='screenshot.png')
# 后续可以对比 screenshot # ...
browser.close()# 打印页面 URLprint(page.url)
# 打印页面标题print(page.title())
# 打印控制台日志page.on('console', lambda msg: print(f'Console: {msg.text}'))
# 打印网络请求page.on('request', lambda req: print(f'Request: {req.url}'))# 有头模式(可以看到浏览器)browser = p.chromium.launch(headless=False)
# 等待调试page.pause()try: page.goto('http://localhost:5173') page.wait_for_load_state('networkidle')except Exception as e: # 截图保存错误状态 page.screenshot(path='error.png') print(f'Error: {e}') raise# 1. 角色选择器(最可靠)page.get_by_role('button', name='提交')page.get_by_role('link', name='关于')
# 2. 文本选择器page.get_by_text('登录')
# 3. 标签选择器page.get_by_label('用户名')page.get_by_placeholder('请输入')# 不推荐:脆弱的 CSS 选择器page.locator('#root > div > button')
# 不推荐:nth-childpage.locator('div:nth-child(3)')核心命令
wait_for_load_state(‘networkidle’)
角色 > 文本 > 标签 > CSS
调试
page.screenshot() page.pause()
模式
侦察 → 识别 → 执行
查看源文件: GitHub原始文件