Playwright 调试技巧 #
调试工具概述 #
Playwright 提供多种调试工具,帮助开发者快速定位和解决测试问题。
调试工具列表 #
text
┌─────────────────────────────────────────────────────────────┐
│ Playwright 调试工具 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 🔧 Trace Viewer - 测试执行追踪查看器 │
│ 🎯 Codegen - 代码生成工具 │
│ 🖥️ UI Mode - 交互式测试运行器 │
│ 💻 VS Code - VS Code 插件 │
│ 🐛 Debug Mode - 调试模式 │
│ 📸 Screenshots - 截图和视频 │
│ │
└─────────────────────────────────────────────────────────────┘
Trace Viewer #
启用追踪 #
typescript
// playwright.config.ts
import { defineConfig } from '@playwright/test';
export default defineConfig({
use: {
// 总是启用追踪
trace: 'on',
// 失败时启用追踪
trace: 'on-first-retry',
// 失败时保留追踪
trace: 'retain-on-failure',
},
});
命令行启用 #
bash
# 启用追踪运行测试
npx playwright test --trace on
# 失败时追踪
npx playwright test --trace on-first-retry
查看追踪 #
bash
# 查看追踪文件
npx playwright show-trace trace.zip
# 查看特定追踪
npx playwright show-trace test-results/test-name/trace.zip
Trace Viewer 功能 #
text
Trace Viewer 界面:
┌─────────────────────────────────────────────────────────────┐
│ 时间线 │
│ ├── 显示所有操作的时间顺序 │
│ └── 可点击跳转到特定操作 │
├─────────────────────────────────────────────────────────────┤
│ 操作列表 │
│ ├── 显示所有页面操作 │
│ ├── 显示网络请求 │
│ └── 显示控制台日志 │
├─────────────────────────────────────────────────────────────┤
│ 快照 │
│ ├── 操作前后的页面快照 │
│ └── 可以查看 DOM 结构 │
├─────────────────────────────────────────────────────────────┤
│ 网络请求 │
│ ├── 请求/响应详情 │
│ └── 请求时间线 │
├─────────────────────────────────────────────────────────────┤
│ 控制台 │
│ ├── 日志输出 │
│ └── 错误信息 │
└─────────────────────────────────────────────────────────────┘
在测试中手动追踪 #
typescript
import { test, expect } from '@playwright/test';
test('手动追踪', async ({ page, context }) => {
// 开始追踪
await context.tracing.start({ screenshots: true, snapshots: true });
await page.goto('/');
await page.click('button');
// 停止追踪并保存
await context.tracing.stop({ path: 'trace.zip' });
});
Codegen - 代码生成 #
启动 Codegen #
bash
# 基本用法
npx playwright codegen
# 指定 URL
npx playwright codegen https://example.com
# 指定浏览器
npx playwright codegen --browser=firefox https://example.com
# 指定设备
npx playwright codegen --device="iPhone 12" https://example.com
# 指定语言
npx playwright codegen --lang=python https://example.com
# 保存到文件
npx playwright codegen --output=test.spec.ts https://example.com
Codegen 窗口 #
text
┌─────────────────────────────────────────────────────────────┐
│ Playwright Inspector │
├─────────────────────────────────────────────────────────────┤
│ Record Stop Copy Clear │
├─────────────────────────────────────────────────────────────┤
│ // 生成的代码 │
│ const { chromium } = require('playwright'); │
│ │
│ (async () => { │
│ const browser = await chromium.launch(); │
│ const page = await browser.newPage(); │
│ await page.goto('https://example.com'); │
│ await page.click('text=More information'); │
│ })(); │
└─────────────────────────────────────────────────────────────┘
Codegen 选项 #
bash
# 仿真设备
npx playwright codegen \
--device="iPhone 12" \
--viewport-size="390,844" \
--color-scheme="dark" \
--lang="zh-CN" \
--timezone="Asia/Shanghai" \
--geolocation="39.9042,116.4074" \
https://example.com
UI 模式 #
启动 UI 模式 #
bash
# 启动 UI 模式
npx playwright test --ui
# 指定测试文件
npx playwright test --ui login.spec.ts
# 指定项目
npx playwright test --ui --project=chromium
UI 模式功能 #
text
┌─────────────────────────────────────────────────────────────┐
│ Playwright UI │
├─────────────────────────────────────────────────────────────┤
│ │
│ 测试列表 │
│ ├── 显示所有测试文件 │
│ ├── 按状态过滤 │
│ └── 搜索测试 │
│ │
│ 测试执行 │
│ ├── 运行单个测试 │
│ ├── 运行所有测试 │
│ └── 调试模式 │
│ │
│ 时间旅行 │
│ ├── 查看每个操作的快照 │
│ ├── 查看操作前后的页面 │
│ └── 查看网络请求 │
│ │
│ 源码查看 │
│ ├── 显示测试代码 │
│ └── 高亮当前执行行 │
│ │
└─────────────────────────────────────────────────────────────┘
UI 模式快捷键 #
| 快捷键 | 功能 |
|---|---|
r |
运行当前测试 |
f |
运行失败的测试 |
t |
运行所有测试 |
p |
暂停/继续 |
s |
步进到下一个操作 |
Esc |
取消当前操作 |
VS Code 插件 #
安装插件 #
- 在 VS Code 中搜索 “Playwright Test for VS Code”
- 点击安装
插件功能 #
text
VS Code 插件功能:
├── 测试资源管理器
│ ├── 显示所有测试
│ ├── 运行/调试测试
│ └── 查看测试结果
│
├── 代码补全
│ ├── Locator 建议
│ ├── 方法补全
│ └── 参数提示
│
├── 调试支持
│ ├── 断点调试
│ ├── 变量查看
│ └── 调用堆栈
│
└── 内联错误
├── 显示错误位置
└── 显示期望值/实际值
VS Code 配置 #
json
// .vscode/settings.json
{
"playwright.env": {
"BASE_URL": "http://localhost:3000"
},
"playwright.project": "chromium",
"playwright.showTrace": true,
"playwright.reuseBrowser": true
}
调试配置 #
json
// .vscode/launch.json
{
"version": "0.2.0",
"configurations": [
{
"type": "pwa-node",
"request": "launch",
"name": "Playwright Debug",
"runtimeExecutable": "npx",
"runtimeArgs": [
"playwright",
"test",
"--project=chromium"
],
"console": "integratedTerminal"
}
]
}
调试模式 #
–debug 标志 #
bash
# 调试模式运行
npx playwright test --debug
# 调试特定测试
npx playwright test login.spec.ts --debug
page.pause() #
typescript
import { test, expect } from '@playwright/test';
test('使用 pause 调试', async ({ page }) => {
await page.goto('/');
// 暂停执行,打开 Inspector
await page.pause();
await page.click('button');
await expect(page.locator('.result')).toBeVisible();
});
调试特定步骤 #
typescript
test('调试特定步骤', async ({ page }) => {
await page.goto('/');
console.log('步骤 1: 页面加载完成');
await page.click('button');
console.log('步骤 2: 按钮点击完成');
// 在关键点暂停
if (process.env.DEBUG) {
await page.pause();
}
await expect(page.locator('.result')).toBeVisible();
console.log('步骤 3: 断言完成');
});
控制台调试 #
输出调试信息 #
typescript
test('控制台调试', async ({ page }) => {
// 监听控制台消息
page.on('console', msg => {
console.log(`浏览器控制台: ${msg.type()}: ${msg.text()}`);
});
// 监听页面错误
page.on('pageerror', error => {
console.error(`页面错误: ${error.message}`);
});
// 监听请求
page.on('request', request => {
console.log(`请求: ${request.method()} ${request.url()}`);
});
// 监听响应
page.on('response', response => {
console.log(`响应: ${response.status()} ${response.url()}`);
});
await page.goto('/');
});
使用 evaluate 调试 #
typescript
test('evaluate 调试', async ({ page }) => {
await page.goto('/');
// 在浏览器上下文中执行代码
const result = await page.evaluate(() => {
console.log('在浏览器中执行');
return {
title: document.title,
url: window.location.href,
elementCount: document.querySelectorAll('*').length,
};
});
console.log('页面信息:', result);
});
截图和视频 #
失败时截图 #
typescript
// playwright.config.ts
export default defineConfig({
use: {
screenshot: 'only-on-failure',
video: 'retain-on-failure',
},
});
手动截图 #
typescript
test('手动截图', async ({ page }) => {
await page.goto('/');
// 整页截图
await page.screenshot({ path: 'screenshot.png' });
// 元素截图
await page.locator('.card').screenshot({ path: 'card.png' });
// 全页截图
await page.screenshot({
path: 'fullpage.png',
fullPage: true
});
});
条件截图 #
typescript
test.afterEach(async ({ page }, testInfo) => {
// 失败时截图
if (testInfo.status !== testInfo.expectedStatus) {
await page.screenshot({
path: `screenshots/${testInfo.title}-failure.png`
});
}
});
常见调试场景 #
元素定位问题 #
typescript
test('调试元素定位', async ({ page }) => {
await page.goto('/');
// 检查元素是否存在
const element = page.locator('.my-element');
const count = await element.count();
console.log(`找到 ${count} 个元素`);
// 检查元素是否可见
const isVisible = await element.isVisible();
console.log(`元素可见: ${isVisible}`);
// 获取元素信息
const text = await element.textContent();
const html = await element.innerHTML();
console.log(`元素文本: ${text}`);
console.log(`元素 HTML: ${html}`);
// 高亮元素(调试用)
await element.evaluate(el => {
el.style.border = '2px solid red';
});
await page.pause();
});
网络请求问题 #
typescript
test('调试网络请求', async ({ page }) => {
// 记录所有请求
const requests: any[] = [];
page.on('request', request => {
requests.push({
url: request.url(),
method: request.method(),
headers: request.headers(),
});
});
// 记录失败请求
page.on('requestfailed', request => {
console.log(`请求失败: ${request.url()}`);
console.log(`失败原因: ${request.failure()?.errorText}`);
});
await page.goto('/');
console.log('所有请求:', requests);
});
等待问题 #
typescript
test('调试等待问题', async ({ page }) => {
await page.goto('/');
// 检查元素状态
const element = page.locator('.dynamic-element');
console.log('等待元素...');
try {
await element.waitFor({ state: 'visible', timeout: 5000 });
console.log('元素已可见');
} catch (error) {
console.log('元素未在超时时间内出现');
// 检查元素是否存在
const count = await element.count();
console.log(`元素数量: ${count}`);
// 检查 DOM 中是否有类似元素
const similar = await page.locator('[class*="dynamic"]').count();
console.log(`类似元素数量: ${similar}`);
}
});
调试最佳实践 #
1. 使用有意义的测试名称 #
typescript
// ✅ 推荐
test('用户使用有效凭据登录后应该跳转到仪表板', async ({ page }) => {
// ...
});
// ❌ 不推荐
test('test1', async ({ page }) => {
// ...
});
2. 添加调试日志 #
typescript
test('带日志的测试', async ({ page }) => {
console.log('=== 测试开始 ===');
await page.goto('/');
console.log('页面加载完成');
await page.click('button');
console.log('按钮点击完成');
console.log('=== 测试结束 ===');
});
3. 使用软断言收集信息 #
typescript
test('软断言调试', async ({ page }) => {
await page.goto('/');
// 收集所有失败
await expect.soft(page.locator('.title')).toHaveText('Welcome');
await expect.soft(page.locator('.subtitle')).toBeVisible();
await expect.soft(page.locator('.content')).toHaveCount(5);
});
4. 保存调试信息 #
typescript
test.afterEach(async ({ page }, testInfo) => {
if (testInfo.status === 'failed') {
// 保存截图
await page.screenshot({
path: `debug/${testInfo.title}.png`
});
// 保存 HTML
const html = await page.content();
require('fs').writeFileSync(
`debug/${testInfo.title}.html`,
html
);
// 保存控制台日志
// ...
}
});
下一步 #
现在你已经掌握了调试技巧,接下来学习 视觉测试 了解如何进行视觉回归测试!
最后更新:2026-03-28