Playwright 教程
1. Playwright 是什么
Playwright 是一个前端自动化测试工具,可以用来:
- 做 Web 自动化测试
- 模拟用户点击、输入、跳转
- 跑 Chrome、Edge、Firefox、Safari(WebKit)
- 截图、录视频、抓日志
- 做接口拦截、Mock 数据
- 跑 E2E 测试、UI 测试、回归测试
它的优点是:
- 跨浏览器
- API 比较现代
- 自动等待机制很好用
- 对单页应用支持不错
- 官方测试框架比较完整
2. 适合谁学
适合这些场景:
- 前端开发想补测试能力
- QA/测试工程师做自动化
- 全栈项目做端到端测试
- 想做网页爬取/自动操作(合规前提下)
3. 安装
3.1 创建项目
mkdir playwright-demo
cd playwright-demo
npm init -y
3.2 安装 Playwright
推荐直接装测试框架版本:
npm init playwright@latest
安装时通常会让你选择:
- JavaScript / TypeScript
- 测试目录
- 是否生成 GitHub Actions
- 是否安装浏览器
如果你已经有项目,也可以手动安装:
npm install -D @playwright/test
npx playwright install
4. 项目结构
初始化后一般像这样:
playwright-demo/
tests/
example.spec.ts
playwright.config.ts
package.json
常见文件说明:
tests/:测试文件目录playwright.config.ts:全局配置package.json:脚本和依赖
5. 第一个测试
新建文件 tests/demo.spec.ts
import { test, expect } from '@playwright/test';
test('首页标题测试', async ({ page }) => {
await page.goto('https://playwright.dev/');
await expect(page).toHaveTitle(/Playwright/);
});
运行测试:
npx playwright test
只跑某个文件:
npx playwright test tests/demo.spec.ts
打开可视化 UI:
npx playwright test --ui
6. 基本概念
6.1 test
定义一个测试用例:
test('测试名称', async ({ page }) => {
// 测试逻辑
});
6.2 page
浏览器中的一个页面标签,相当于一个 tab。
6.3 expect
断言工具,用来验证结果是否符合预期。
7. 常用页面操作
7.1 打开页面
await page.goto('https://example.com');
7.2 点击元素
await page.locator('button').click();
7.3 输入内容
await page.locator('#username').fill('admin');
await page.locator('#password').fill('123456');
7.4 按键操作
await page.locator('#search').press('Enter');
7.5 获取文本
const text = await page.locator('.title').textContent();
7.6 选择下拉框
await page.locator('select').selectOption('option1');
7.7 勾选复选框
await page.locator('#agree').check();
8. Locator 定位器
Playwright 推荐优先用 语义化定位,而不是一堆脆弱的 CSS 路径。
推荐写法
page.getByRole('button', { name: '登录' })
page.getByLabel('用户名')
page.getByPlaceholder('请输入手机号')
page.getByText('提交')
page.getByTestId('submit-btn')
示例
await page.getByLabel('用户名').fill('zhangsan');
await page.getByLabel('密码').fill('123456');
await page.getByRole('button', { name: '登录' }).click();
不太推荐但也常用
page.locator('.login-btn')
page.locator('#username')
page.locator('//button[text()="登录"]')
最佳实践
优先顺序建议:
getByRolegetByLabelgetByPlaceholdergetByTestIdlocator(css/xpath)
9. 断言
9.1 标题断言
await expect(page).toHaveTitle(/Playwright/);
9.2 URL 断言
await expect(page).toHaveURL(/dashboard/);
9.3 文本断言
await expect(page.getByText('登录成功')).toBeVisible();
9.4 输入框值断言
await expect(page.locator('#username')).toHaveValue('admin');
9.5 元素显示/隐藏
await expect(page.locator('.loading')).toBeHidden();
await expect(page.locator('.success')).toBeVisible();
10. 自动等待
Playwright 一个很好用的点是自动等待元素可操作。
比如:
await page.getByRole('button', { name: '提交' }).click();
它会自动等按钮出现、可见、可点击。
所以一般不要滥用 waitForTimeout:
await page.waitForTimeout(5000); // 不推荐
推荐等待明确条件:
await expect(page.getByText('提交成功')).toBeVisible();
或者:
await page.locator('.result').waitFor();
11. 登录测试示例
import { test, expect } from '@playwright/test';
test('用户登录', async ({ page }) => {
await page.goto('https://example.com/login');
await page.getByLabel('用户名').fill('testuser');
await page.getByLabel('密码').fill('123456');
await page.getByRole('button', { name: '登录' }).click();
await expect(page).toHaveURL(/dashboard/);
await expect(page.getByText('欢迎你')).toBeVisible();
});
12. 录制脚本
Playwright 自带代码生成器,特别适合入门。
npx playwright codegen https://example.com
你在浏览器里操作,它会自动生成脚本。
适合:
- 快速熟悉 API
- 快速拿定位器
- 生成初版测试代码
但生成的代码通常要手工整理,别直接长期拿来维护。
13. 调试方法
13.1 暂停调试
await page.pause();
运行时:
npx playwright test --debug
13.2 headed 模式
默认是无头浏览器,可改成有界面:
npx playwright test --headed
13.3 查看 Trace
Playwright 的 trace 很强,失败后可以看完整过程。
运行后查看:
npx playwright show-trace trace.zip
14. 截图和视频
截图
await page.screenshot({ path: 'screenshot.png', fullPage: true });
配置失败时保留截图/视频
在 playwright.config.ts:
use: {
screenshot: 'only-on-failure',
video: 'retain-on-failure',
trace: 'on-first-retry',
}
15. 多浏览器执行
Playwright 可以同时跑 Chromium / Firefox / WebKit。
配置文件示例:
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests',
use: {
headless: true,
},
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
{
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
},
{
name: 'webkit',
use: { ...devices['Desktop Safari'] },
},
],
});
运行全部浏览器:
npx playwright test
只跑某一个:
npx playwright test --project=chromium
16. Hooks 钩子
每个测试前执行
test.beforeEach(async ({ page }) => {
await page.goto('https://example.com/login');
});
全部测试前执行
test.beforeAll(async () => {
// 初始化数据
});
17. 复用登录状态
如果每条测试都重新登录,会很慢。 可以把登录状态存起来。
保存登录态
import { test as setup } from '@playwright/test';
setup('authenticate', async ({ page }) => {
await page.goto('https://example.com/login');
await page.getByLabel('用户名').fill('admin');
await page.getByLabel('密码').fill('123456');
await page.getByRole('button', { name: '登录' }).click();
await page.context().storageState({ path: 'playwright/.auth/user.json' });
});
使用登录态
use: {
storageState: 'playwright/.auth/user.json',
}
这在真实项目里很常见。
18. 接口监听与 Mock
18.1 监听响应
const response = await page.waitForResponse(resp =>
resp.url().includes('/api/user') && resp.status() === 200
);
18.2 Mock 接口
await page.route('**/api/user', async route => {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
name: '老张',
age: 18
}),
});
});
适合:
- 前后端并行开发
- 测试异常状态
- 测试边界场景
19. 文件上传和下载
上传文件
await page.locator('input[type="file"]').setInputFiles('./test-data/a.png');
下载文件
const downloadPromise = page.waitForEvent('download');
await page.getByText('下载').click();
const download = await downloadPromise;
await download.saveAs('./downloads/report.xlsx');
20. iframe 处理
const frame = page.frameLocator('#my-frame');
await frame.getByRole('button', { name: '提交' }).click();
21. 新窗口处理
const [newPage] = await Promise.all([
page.waitForEvent('popup'),
page.getByText('打开新窗口').click(),
]);
await expect(newPage).toHaveURL(/new-page/);
22. 页面对象模型 POM
项目变大后,不要把定位器和操作全堆在测试里。 可以封装成页面对象。
pages/LoginPage.ts
import { Page } from '@playwright/test';
export class LoginPage {
constructor(private page: Page) {}
async goto() {
await this.page.goto('https://example.com/login');
}
async login(username: string, password: string) {
await this.page.getByLabel('用户名').fill(username);
await this.page.getByLabel('密码').fill(password);
await this.page.getByRole('button', { name: '登录' }).click();
}
}
测试里使用
import { test, expect } from '@playwright/test';
import { LoginPage } from '../pages/LoginPage';
test('POM 登录测试', async ({ page }) => {
const loginPage = new LoginPage(page);
await loginPage.goto();
await loginPage.login('admin', '123456');
await expect(page).toHaveURL(/dashboard/);
});
23. 常用命令总结
# 初始化
npm init playwright@latest
# 安装浏览器
npx playwright install
# 运行测试
npx playwright test
# 跑某个文件
npx playwright test tests/demo.spec.ts
# 只跑某个浏览器
npx playwright test --project=chromium
# 有界面运行
npx playwright test --headed
# 调试模式
npx playwright test --debug
# 可视化 UI
npx playwright test --ui
# 录制脚本
npx playwright codegen https://example.com
24. 学习路线建议
建议按这个顺序学:
第一步:先会跑起来
- 安装 Playwright
- 跑官方示例
- 能写一个最简单测试
第二步:掌握基础操作
gotoclickfillexpect- Locator
第三步:掌握真实项目能力
- 登录流程
- 等待机制
- 截图 / Trace / Debug
- 多浏览器运行
第四步:进阶
- POM 封装
- 登录态复用
- 接口 Mock
- CI 集成
25. 新手最常见的坑
坑 1:乱用 CSS/XPath
定位太脆弱,页面一改就挂。
建议:
优先用 getByRole、getByLabel、getByTestId
坑 2:强行 sleep
await page.waitForTimeout(3000);
这样很不稳定。
建议: 等元素、等 URL、等响应、等断言成立。
坑 3:测试之间互相依赖
上一条测试创建的数据,下一条测试又依赖它,容易连锁失败。
建议: 测试尽量独立。
坑 4:一个测试写太长
一个 case 里塞十几个流程,不好维护。
建议: 一个测试验证一个核心目标。
26. 一份实战模板
import { test, expect } from '@playwright/test';
test.describe('用户中心', () => {
test.beforeEach(async ({ page }) => {
await page.goto('https://example.com/login');
await page.getByLabel('用户名').fill('admin');
await page.getByLabel('密码').fill('123456');
await page.getByRole('button', { name: '登录' }).click();
await expect(page).toHaveURL(/dashboard/);
});
test('查看个人资料', async ({ page }) => {
await page.getByText('个人中心').click();
await expect(page.getByText('基本信息')).toBeVisible();
});
test('修改昵称', async ({ page }) => {
await page.getByText('个人中心').click();
await page.getByLabel('昵称').fill('新昵称');
await page.getByRole('button', { name: '保存' }).click();
await expect(page.getByText('保存成功')).toBeVisible();
});
});
27. 一句话理解 Playwright
你可以把 Playwright 理解成:
“用代码操控浏览器,像真人一样操作页面,并验证结果对不对。”
28. 推荐资料
- 官方文档: playwright.dev/
- API 文档: playwright.dev/docs/api/cl…
- 官方测试文档: playwright.dev/docs/writin…