Перехват и мокирование сети в Playwright

В этой главе рассказывается, как использовать Playwright для перехвата, изменения и мокирования сетевых запросов, что позволяет проводить независимое тестирование, не полагаясь на серверные API.


Что такое перехват сетевых запросов

Перехват сетевых запросов позволяет вмешиваться, когда браузер выполняет HTTP-запросы.

Вы можетеблокировать запросы, изменять ответы и возвращать фиктивные данные, делая тесты независимыми от внешних API или состояния сети.


page.route() Перехват запросов

page.route(url, handler)Используется для перехвата всех сетевых запросов, соответствующих указанному шаблону URL.

Базовый перехват

Пример

test('Перехватить все запросы изображений', async ({ page }) => {
  // Перехватить все изображения 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() Прерывание запросов

Пример

test('Блокировать сторонние аналитические скрипты', async ({ page }) => {
  // Блокировать аналитические скрипты, такие как 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-данные

Пример

test('Моковый API списка пользователей', async ({ page }) => {
  // Перехватить 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
});

Моковые ответы с ошибками

Пример

test('Тестирование обработки ошибок API', async ({ page }) => {
  // Имитировать ошибку сервера 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() Изменить и продолжить

Вместо того чтобы напрямую возвращать фиктивные данные, он изменяет реальный запрос или ответ, а затем продолжает отправлять его на сервер.

Пример

test('Изменение заголовков запроса', async ({ page }) => {
  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()чтобы фактически отправить запрос, получить реальный ответ, а затем изменить его.

Пример

test('Изменить реальный ответ', async ({ page }) => {
  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()Все страницы в этом контекстеПравила мокирования, общие для нескольких страниц

Пример

test.beforeEach(async ({ context }) => {
  // Перехват во всём контексте; применяется ко всем страницам
  await context.route('**/*.css', route => route.abort());
});

test('Тест 1', async ({ page }) => { /* Без CSS */ });
test('Тест 2', async ({ page }) => { /* Без CSS */ });

Запись и воспроизведение HAR-файлов

Файл HAR (HTTP Archive) записывает все сетевые запросы страницы, а затем может воспроизводить их или мокировать.

Пример

test('Использовать HAR-файл для мокирования', async ({ page }) => {
  // Загрузить сетевые записи из 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, не завися от реального сервера.

Другие расширения