Перехват и мокирование сети в Playwright
В этой главе рассказывается, как использовать Playwright для перехвата, изменения и мокирования сетевых запросов, что позволяет проводить независимое тестирование, не полагаясь на серверные API.
Что такое перехват сетевых запросов
Перехват сетевых запросов позволяет вмешиваться, когда браузер выполняет HTTP-запросы.
Вы можетеблокировать запросы, изменять ответы и возвращать фиктивные данные, делая тесты независимыми от внешних API или состояния сети.
page.route() Перехват запросов
page.route(url, handler)Используется для перехвата всех сетевых запросов, соответствующих указанному шаблону URL.
Базовый перехват
Пример
// Перехватить все изображения PNG и JPEG
await page.route('**/*.{png,jpg,jpeg}', route => route.abort());
// Перейти на страницу (изображения не будут загружены)
await page.goto('https://www.example.com/');
// Страница может загрузиться быстрее, поскольку изображения не скачиваются
});
Шаблоны сопоставления URLПоддерживаются следующие форматы:
| Паттерн | Пример | Описание |
|---|---|---|
| Полный URL | 'https://api.example.com/users' | Точное совпадение |
| Glob-шаблон | '**/api/**' | **Совпадает с любым путём |
| Регулярное выражение | /\/api\/v\d\/users/ | Гибкое сопоставление |
| Функция | url => url.includes('/api/') | Сопоставление на основе программной логики |
route.abort() Прерывание запросов
Пример
// Блокировать аналитические скрипты, такие как Google Analytics
await page.route('**/*analytics*', route => route.abort());
// Блокировать CSS-файлы
await page.route('**/*.css', route => route.abort());
await page.goto('https://www.example.com/');
// Страница загружается без стилей, но функциональные тесты не затрагиваются
});
route.fulfill() Имитация ответов
route.fulfill()Это самый мощный метод мокирования, способный возвращать любой пользовательский ответ.
Моковые JSON-данные
Пример
// Перехватить API списка пользователей и вернуть моковые данные
await page.route('**/api/users', async route => {
await route.fulfill({
status: 200,
contentType: 'application/json',
json: [
{ id: 1, name: 'example_user', email: '[email protected]' },
{ id: 2, name: 'test_user', email: '[email protected]' },
],
});
});
await page.goto('https://www.example.com/users');
// Страница отобразит моковые данные вместо реальных данных API
});
Моковые ответы с ошибками
Пример
// Имитировать ошибку сервера 500
await page.route('**/api/data', async route => {
await route.fulfill({
status: 500,
contentType: 'application/json',
json: { error: 'Внутренняя ошибка сервера' },
});
});
await page.goto('https://www.example.com/dashboard');
// Убедиться, что сообщение об ошибке отображается корректно
await expect(page.getByText('Внутренняя ошибка сервера')).toBeVisible();
});
route.continue() Изменить и продолжить
Вместо того чтобы напрямую возвращать фиктивные данные, он изменяет реальный запрос или ответ, а затем продолжает отправлять его на сервер.
Пример
await page.route('**/api/**', async (route, request) => {
// Изменить заголовки запроса и добавить пользовательские заголовки
const headers = {
...request.headers(),
'X-Custom-Header': 'EXAMPLE-TEST',
};
await route.continue({ headers });
});
await page.goto('https://www.example.com/');
});
test('Изменить POST-данные', async ({ page }) => {
await page.route('**/api/login', async (route, request) => {
// Переопределить тело POST-запросов
const postData = JSON.parse(request.postData() || '{}');
postData.mode = 'test'; // Добавить тестовый маркер
await route.continue({
postData: JSON.stringify(postData),
});
});
});
route.fetch() Реальные запросы
В функции-обработчике мока вы можете использоватьroute.fetch()чтобы фактически отправить запрос, получить реальный ответ, а затем изменить его.
Пример
await page.route('**/api/products', async route => {
// Сначала получить реальный ответ
const response = await route.fetch();
const body = await response.json();
// Изменить данные ответа
body.items[0].name = 'ПРИМЕР изменённого названия товара';
// Вернуть изменённые данные
await route.fulfill({
status: response.status(),
contentType: 'application/json',
json: body,
});
});
});
Перехват на уровне контекста против перехвата на уровне страницы
| Метод | Область действия | Сценарии использования |
|---|---|---|
page.route() | Только для текущей страницы | Мокирование сети для одной страницы |
context.route() | Все страницы в этом контексте | Правила мокирования, общие для нескольких страниц |
Пример
// Перехват во всём контексте; применяется ко всем страницам
await context.route('**/*.css', route => route.abort());
});
test('Тест 1', async ({ page }) => { /* Без CSS */ });
test('Тест 2', async ({ page }) => { /* Без CSS */ });
Запись и воспроизведение HAR-файлов
Файл HAR (HTTP Archive) записывает все сетевые запросы страницы, а затем может воспроизводить их или мокировать.
Пример
// Загрузить сетевые записи из HAR-файла и воспроизвести
await page.routeFromHAR('tests/hars/api-responses.har');
await page.goto('https://www.example.com/');
// Все совпадающие запросы будут получать ответы из HAR-файла
});
# 录制 HAR 文件 npx playwright open --save-har=tests/hars/api-responses.har https://www.example.com/
Другие расширенияHAR-файлы подходят для имитации больших или сложных ответов API. Запишите один раз, и последующие тесты воспроизводятся из HAR, не завися от реального сервера.