Playwright 网络拦截
Playwright 的网络拦截 (Network Interception) 是自动化测试和爬虫场景中非常有用的能力,可以让我们拦截、修改、模拟网络请求与响应。
Playwright 提供了 page.route()
和 page.on('request')
/ page.on('response')
等 API,可以:
- 拦截请求:决定是否放行、修改 URL/headers/body、或者直接返回 mock 响应。
- 监听请求/响应:查看请求的 URL、方法、头部、响应状态码、响应体等。
- 模拟网络错误:阻止请求、返回错误码,测试应用的容错逻辑。
常见应用场景:
- Mock API:不用后端接口也能跑前端 UI 测试。
- 修改请求:比如加 token,测试异常 header。
- 性能调试:统计请求数、响应时间。
- 错误注入:模拟 500/404 等服务端异常。
拦截请求:page.route()
await page.route('**/api/todos', async route => { // 拦截匹配的请求 console.log('拦截到请求:', route.request().url()); // 1. 直接放行(继续请求) await route.continue(); // 2. 修改请求 // await route.continue({ headers: { ...route.request().headers(), 'X-Test': 'true' } }); // 3. 返回 mock 响应 // await route.fulfill({ // status: 200, // contentType: 'application/json', // body: JSON.stringify([{ id: 1, text: 'mock todo', done: false }]), // }); // 4. 拒绝请求 // await route.abort(); });
参数说明:
page.route(url, handler)
:注册一个拦截器,url
支持字符串、正则或**
通配。route.request()
:获取请求对象。route.continue([options])
:放行请求,可以修改方法、headers、postData。route.fulfill(response)
:直接返回伪造的响应。route.abort([errorCode])
:中断请求,常见错误码如"failed"
,"aborted"
,"timedout"
。
监听请求和响应
// 监听所有请求 page.on('request', req => { console.log(`➡️ 请求: ${req.method()} ${req.url()}`); }); // 监听所有响应 page.on('response', async res => { console.log(`⬅️ 响应: ${res.status()} ${res.url()}`); }); // 获取响应 body const response = await page.waitForResponse('**/api/data'); console.log('响应内容:', await response.text());
常用方法:
request.url()
/request.method()
/request.headers()
/request.postData()
response.status()
/response.headers()
/response.text()
/response.json()
模拟网络环境
实例
// 模拟网络错误
await page.route('**/*.png', route => route.abort('failed'));
// 模拟延迟
await page.route('**/api/**', async route => {
await new Promise(r => setTimeout(r, 2000));
await route.continue();
});
</pre>
<hr>
<h2>完整示例:Mock API</h2>
import { test, expect } from '@playwright/test';
test('拦截并 mock API', async ({ page }) => {
// 拦截 /api/todos 请求并返回固定数据
await page.route('**/api/todos', async route => {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify([
{ id: 1, text: 'Playwright 学习', done: false },
{ id: 2, text: '写自动化测试', done: true },
]),
});
});
await page.goto('https://example-todo-app.com');
const items = await page.locator('.todo-item').allTextContents();
console.log('加载到的待办事项:', items);
// 断言 UI 显示 mock 数据
await expect(page.locator('.todo-item')).toHaveCount(2);
await expect(page.locator('.todo-item').first()).toContainText('Playwright 学习');
});
await page.route('**/*.png', route => route.abort('failed'));
// 模拟延迟
await page.route('**/api/**', async route => {
await new Promise(r => setTimeout(r, 2000));
await route.continue();
});
</pre>
<hr>
<h2>完整示例:Mock API</h2>
import { test, expect } from '@playwright/test';
test('拦截并 mock API', async ({ page }) => {
// 拦截 /api/todos 请求并返回固定数据
await page.route('**/api/todos', async route => {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify([
{ id: 1, text: 'Playwright 学习', done: false },
{ id: 2, text: '写自动化测试', done: true },
]),
});
});
await page.goto('https://example-todo-app.com');
const items = await page.locator('.todo-item').allTextContents();
console.log('加载到的待办事项:', items);
// 断言 UI 显示 mock 数据
await expect(page.locator('.todo-item')).toHaveCount(2);
await expect(page.locator('.todo-item').first()).toContainText('Playwright 学习');
});
网络拦截相关 API
API | 用途 | 常见参数/说明 |
---|---|---|
page.route(url, handler) |
注册请求拦截 | url 可为字符串/正则/通配符 |
route.request() |
获取请求对象 | 返回 Request |
route.continue([options]) |
放行请求,可修改 | { method, headers, postData } |
route.fulfill(response) |
返回自定义响应 | { status, headers, body, contentType } |
route.abort([errorCode]) |
中断请求 | 常见:'failed' , 'aborted' , 'timedout' |
page.unroute(url, handler?) |
移除拦截规则 | |
page.on('request', cb) |
监听请求 | |
page.on('response', cb) |
监听响应 | |
request.url()/method()/headers()/postData() |
请求信息 | |
response.status()/headers()/text()/json() |
响应信息 | |
page.waitForResponse(urlOrPredicate) |
等待特定响应 | 可传 URL 或函数 |
点我分享笔记