Playwright 测试
本章带领你编写并运行第一个 Playwright 测试,理解测试的基本组成和执行流程。
运行示例测试
初始化项目后,先运行一下自动生成的示例测试,确认一切正常:
# 运行所有测试 npx playwright test
运行后你会看到类似以下的输出:
Running 6 tests using 4 workers ✓ 1 [chromium] › tests/example.spec.ts:3:1 › has title (2.1s) ✓ 2 [chromium] › tests/example.spec.ts:10:1 › get started link (1.8s) ✓ 3 [firefox] › tests/example.spec.ts:3:1 › has title (1.9s) ✓ 4 [firefox] › tests/example.spec.ts:10:1 › get started link (1.7s) ✓ 5 [webkit] › tests/example.spec.ts:3:1 › has title (2.3s) ✓ 6 [webkit] › tests/example.spec.ts:10:1 › get started link (2.0s) 6 passed (12s)
从输出中可以看到:测试在 3 个浏览器上各运行了 2 个测试,共 6 个测试全部通过。
创建第一个测试文件
清空 tests/example.spec.ts 的内容,我们来从头写一个测试。
实例
import { test, expect } from '@playwright/test';
// test(name, callback) 定义一个测试
// name:测试名称,会显示在测试报告中
// callback 的参数 { page } 是 Playwright 提供的 fixture(夹具)
test('访问 RUNOOB 首页并检查标题', async ({ page }) => {
// 导航到指定 URL
await page.goto('https://www.runoob.com/');
// 断言:页面标题包含 "RUNOOB"
await expect(page).toHaveTitle(/RUNOOB/);
});
运行这个测试:
npx playwright test tests/first-test.spec.ts
预期输出:
Running 1 test using 1 worker ✓ 1 [chromium] › tests/first-test.spec.ts:4:1 › 访问 RUNOOB 首页并检查标题 (2.5s) 1 passed (2.5s)
test() 函数详解
test() 是 Playwright Test 的核心函数,用于定义一个测试用例。
基本语法
实例
// test(测试名称, 测试函数)
test('测试的描述性名称', async ({ page }) => {
// 测试中的操作
await page.goto('https://example.com');
// 测试中的断言
await expect(page).toHaveTitle(/Example/);
});
参数说明
| 参数 | 类型 | 说明 |
|---|---|---|
| 第一个参数(测试名称) | string | 测试的描述性名称,会显示在测试报告和日志中 |
| 第二个参数(测试函数) | async (fixtures) => {} | 异步函数,包含测试的实际逻辑 |
page fixture 是什么
{ page } 是测试函数参数中的解构赋值,它从 Playwright 提供的 fixtures 对象中提取 page。
page 是一个 Page 对象,代表了一个浏览器标签页。
每个测试都拥有自己独立的 page 实例:
| fixture | 类型 | 说明 |
|---|---|---|
page | Page 对象 | 独立的浏览器标签页,用于导航、操作、断言 |
context | BrowserContext 对象 | 浏览器上下文,管理 Cookie、Storage 等 |
browser | Browser 对象 | 浏览器实例,较少直接使用 |
request | APIRequestContext 对象 | 用于发送 HTTP 请求(不通过浏览器) |
最常用的是 page,绝大多数操作都通过它完成。
page.goto() 导航
page.goto(url) 是 Playwright 中最基础的操作之一,用来导航到指定 URL。
实例
await page.goto('https://www.runoob.com/');
// 带选项的导航
await page.goto('https://www.runoob.com/', {
// 等待直到 'load' 事件触发(默认值)
waitUntil: 'load',
// 操作超时时间(毫秒)
timeout: 30000,
// 引用来源(Referer 头)
referer: 'https://www.google.com/',
});
waitUntil 选项有三个可选值:
| 值 | 含义 | 适用场景 |
|---|---|---|
'load' | 等待 load 事件触发 | 默认值,适用于大多数页面 |
'domcontentloaded' | 等待 DOMContentLoaded 事件 | 页面基本结构加载完成即可 |
'networkidle' | 等待网络空闲(500ms 内无新请求) | 需要等待异步数据全部加载完毕 |
在绝大多数情况下使用默认的
'load'即可,Playwright 的自动等待机制会处理后续的元素可用性问题,无需使用'networkidle'。
expect() 断言
expect() 是 Playwright 的断言方法,用来验证测试结果是否符合预期。
实例
await expect(page).toHaveTitle(/RUNOOB/);
// 页面 URL 断言
await expect(page).toHaveURL('https://www.runoob.com/');
// 元素可见性断言
await expect(page.getByText('RUNOOB')).toBeVisible();
Playwright 的断言有一个关键特性:自动重试。
如果断言条件暂时不满足,Playwright 会不断重试,直到条件满足或超时。
这意味着你不需要写 waitFor 或 sleep 来等待页面就绪。
测试的 async/await 模式
Playwright 的所有操作都是异步的,因此测试函数必须声明为 async。
所有浏览器操作(goto、click、fill 等)和断言(expect)都必须使用 await 关键字。
实例
test('正确示例', async ({ page }) => {
await page.goto('https://www.runoob.com/');
await expect(page).toHaveTitle(/RUNOOB/);
});
// 错误写法(缺少 await)
test('错误示例', async ({ page }) => {
page.goto('https://www.runoob.com/'); // 没有 await!
expect(page).toHaveTitle(/RUNOOB/); // 没有 await!
});
如果忘记写
await,测试可能会在不正确的时机断言,导致测试不稳定或意外通过。务必在每次调用 Playwright API 时加上await。
理解测试输出
我们再看一个稍带断言失败的测试,以理解测试输出:
实例
import { test, expect } from '@playwright/test';
test('一个故意失败的测试', async ({ page }) => {
await page.goto('https://www.runoob.com/');
// 断言标题包含不存在的内容,预期会失败
await expect(page).toHaveTitle(/不存在的文字/);
});
运行后,输出大致如下:
Running 1 test using 1 worker
✘ 1 [chromium] › tests/fail-demo.spec.ts:4:1 › 一个故意失败的测试 (5.2s)
1 failed
tests/fail-demo.spec.ts:4:1 › 一个故意失败的测试
同时会自动打开 HTML 报告,显示详细的失败信息和错误截图。
