Перейти к основному контенту
Tech Path Finder
КурсыИнтервьюКод-ревьюБлог
Tech Path Finder

Персонализированный путеводитель в IT. Квизы, мок-интервью, код ревью и аналитика прогресса.

@potapov_me

Платформа

  • Курсы
  • Прогресс
  • Мок-интервью
  • Код ревью
  • Живое ревью с ИИ
  • Тренажёр переговоров
  • Закладки

Контент

  • Блог
  • Главная
  • Обратная связь

Компания

  • О проекте
  • Тарифы
  • Условия использования
  • Конфиденциальность
  • Согласие на обработку данных
  • Cookie
  • Реквизиты

Аккаунт

  • Войти
  • Зарегистрироваться
  • Профиль

© 2026 Tech Path Finder. Все права защищены.

·ИП Потапов К.С.·Политика конфиденциальности·
Сделано с ❤️ в России
  1. Mocking и перехват запросов
network

Mocking и перехват запросов

Перехват сетевых запросов, стабы, моки API, модификация ответов сервера

Mocking и перехват запросов

Перехват сетевых запросов — одна из самых мощных возможностей Playwright. Вы можете заглушить API, подменить ответ, замедлить запрос или вернуть ошибку — всё это без изменения кода приложения.

#1. Зачем мокировать API?

Тестирование UI не должно зависеть от бэкенда. Моки дают:

  • Независимость — тесты работают без запущенного сервера
  • Скорость — не нужно ждать реальных API-ответов
  • Предсказуемость — ответ всегда одинаковый, нет race condition
  • Полнота — можно протестировать обработку ошибок (500, таймауты)

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

page.route(pattern, handler) перехватывает запрос до отправки:

# Заглушить API пользователей await page.route('**/api/users', lambda route: route.fulfill( status=200, json=[ {'id': 1, 'name': 'Иван', 'email': 'ivan@example.com'}, {'id': 2, 'name': 'Мария', 'email': 'maria@example.com'}, ], )) await page.goto('/users') # Страница получает заглушённые данные assert page.get_by_text('Иван').is_visible() assert page.get_by_text('Мария').is_visible()

Глоб-паттерн **/api/users совпадает с любым URL, заканчивающимся на /api/users.

#3. Три действия handler'а

Handler получает объект route и обязан выполнить одно из трёх действий:

# 1. Вернуть подставной ответ await page.route('**/api/data', lambda route: route.fulfill( status=200, json={'key': 'value'}, )) # 2. Пропустить запрос к серверу await page.route('**/api/analytics', lambda route: route.continue_()) # 3. Отменить запрос (имитация ошибки сети) await page.route('**/api/tracking', lambda route: route.abort('failed'))

Важно: handler обязан вызвать одно из трёх действий. Если handler ничего не вызовет, запрос зависнет до таймаута.

#4. Динамический handler

Вместо lambda используйте обычную функцию для сложной логики:

def handle_users_route(route): # Можно читать тело запроса post_data = route.request.post_data_json if post_data and post_data.get('role') == 'admin': route.fulfill(status=200, json={'id': 1, 'role': 'admin'}) else: route.fulfill(status=200, json={'id': 2, 'role': 'viewer'}) await page.route('**/api/users', handle_users_route)

#5. Фильтрация по методу и URL

Передайте функцию вместо строки для точной фильтрации:

def only_post_requests(route): return route.request.method == 'POST' and '/api/users' in route.request.url await page.route(only_post_requests, lambda route: route.fulfill( status=201, json={'id': 42, 'created': True}, ))

#6. Модификация запроса перед отправкой

route.continue_() позволяет изменить запрос:

# Подменить заголовок авторизации await page.route('**/api/**', lambda route: route.continue_( headers={ **route.request.headers, 'Authorization': 'Bearer test-token', } )) # Изменить метод await page.route('**/api/old-endpoint', lambda route: route.continue_( method='POST', url='https://api.example.com/new-endpoint', ))

#7. Имитация задержки сети

Параметр delay замедляет ответ — полезно для тестирования лоадеров:

await page.route('**/api/slow-data', lambda route: route.fulfill( status=200, json={'data': 'loaded'}, delay=2000, # 2 секунды задержки )) await page.goto('/dashboard') # Проверяем, что лоадер виден assert page.locator('.spinner').is_visible() # Ждём данные assert page.get_by_text('loaded').is_visible()

#8. Модификация ответа сервера

Иногда нужно не заменить ответ полностью, а изменить часть реального ответа:

async def modify_response(route): # Пропускаем запрос к серверу response = await route.fetch() # Читаем JSON data = await response.json() # Модифицируем data['features'].append('premium') # Возвращаем изменённый ответ await route.fulfill( status=response.status, json=data, ) await page.route('**/api/config', modify_response)

route.fetch() выполняет реальный запрос к серверу и возвращает APIResponse. Это позволяет читать ответ, модифицировать и возвращать изменённую версию.

#9. Мокирование ошибки сервера

# Ошибка 500 — Internal Server Error await page.route('**/api/crash', lambda route: route.fulfill( status=500, json={'error': 'Internal Server Error'}, )) # Ошибка 403 — Forbidden await page.route('**/api/admin', lambda route: route.fulfill( status=403, json={'error': 'Доступ запрещён'}, )) # Сетевая ошибка — как будто сервер недоступен await page.route('**/api/offline', lambda route: route.abort('failed'))

Тестирование обработки ошибок:

await page.route('**/api/submit', lambda route: route.fulfill(status=500)) await page.get_by_role('button', name='Отправить').click() assert page.get_by_text('Произошла ошибка').is_visible()

#10. Снятие моков

# Снять все моки await page.unroute_all() # Снять мок для конкретного паттерна await page.unroute('**/api/users') # Снять мок с handler'ом (если их несколько) await page.unroute('**/api/users', specific_handler)

unroute_all() полезен в teardown фикстуры, чтобы мок одного теста не повлиял на другой.

#11. Перехват в контексте (рекомендуемый подход)

Для чистоты тестов используйте контекстный менеджер page.expect_request():

# Ожидание, что тест вызовет конкретный запрос with page.expect_request('**/api/save') as request_info: await page.get_by_role('button', name='Сохранить').click() request = request_info.value assert request.method == 'POST' assert 'important_data' in request.post_data_json

#12. Типичные ошибки

❌ Забыть вызвать fulfill, continue_ или abort. Запрос зависнет до таймаута (30 секунд). Handler обязан завершить запрос.

❌ Использовать fulfill(status=500) для имитации недоступности сервера. HTTP 500 — это ответ сервера. Сетевая ошибка — это abort('failed'). Для UI это разные сценарии: при 500 страница может показать одно сообщение, при network error — другое.

❌ Один мок на все тесты в файле. Моки, установленные в одном тесте, могут повлиять на другие. Снимайте моки в teardown: yield + await page.unroute_all().

✅ Правильный подход: устанавливать моки в фикстуре или начале теста, снимать в teardown, использовать expect_request для проверки исходящих запросов.


#Упражнения

  1. Напишите тест для страницы /users: заглушите GET /api/users и проверьте, что имена отображаются.
  2. Протестируйте обработку ошибки 500: заглушите POST /api/submit статусом 500 и проверьте сообщение об ошибке.
  3. Используйте route.fetch() для модификации реального ответа: добавьте поле в JSON перед возвратом.
  4. Протестируйте loader: заглушите API с delay=3000 и проверьте, что спиннер виден до загрузки данных.

#Что дальше?

Изучите аутентификацию и авторизацию, чтобы научиться сохранять сессии и обходить login-формы.

Далее: Аутентификация и авторизация