Назад к блогу
Начало работы

Сетевое мокирование для собственных тестов браузерной автоматизации

Делайте разрешённые тесты браузера детерминированными с помощью контролируемых сетевых двойников и сохраняйте границы production и сторонних сервисов.

Документация

Нужна структурированная документация по теме Начало работы?

Эта статья относится к редакционной библиотеке. Для пошаговой настройки, справки и постоянных обновлений переходите сразу в соответствующий раздел docs.

Собственный браузерный тест направляет синтетический запрос в контролируемый ответ и сохраняет ограниченный результат

Сетевое мокирование полезно, когда тест владеет страницей и зависимостью приложения, которую проверяет. Контролируемый ответ позволяет воспроизводить загрузку, пустой результат, ошибку валидации или временную аварию без ожидания удалённого сервиса. Граница входит в контракт теста: mock является двойником разрешённой зависимости приложения, а не способом изменить сторонний сайт, обойти политику или заявить, что production работал так же.

Сначала опишите пользовательский контракт: маршрут, метод и форму запроса, ожидаемое видимое состояние и разрешённые свидетельства. Затем решите, какой зависимостью владеет тест и какая настоящая интеграция требует отдельной проверки. Используйте руководство Playwright для жизненного цикла браузера и руководство по скачиванию и загрузке для артефактов. Руководство Playwright по API mocking и практики Selenium описывают механику фреймворков, но синтетический ответ не доказывает поведение удалённого сервиса.

Определите границу владения

Перехватывайте только запрос, принадлежащий сценарию. Используйте синтетическую учётную запись, тестовый origin и узкий шаблон маршрута, который не захватывает аналитику, аутентификацию, телеметрию и чужие ресурсы. Обработчик проверяет метод и нужные поля, возвращает документированный ответ и записывает короткую категорию результата. Не сохраняйте токены, cookie, заголовки авторизации, полные тела, текст страницы или скопированное production-тело ответа.

Отделяйте production-путь. Держите обработчики в тестовом коде или тестовом stub-сервере, включайте их явной опцией и завершайте тест с ошибкой, если опция попала в production-сборку. Называйте сценарий в результате, чтобы ревьюер отличал синтетический ответ от живой интеграции. Если браузерный worker или кэш не пропустил запрос, запишите это. Отсутствие совпадения с обработчиком описывает путь браузера, но не подтверждает вызов сервиса.

Опишите детерминированный fixture

До реализации обработчика зафиксируйте четыре факта:

  • Вход: синтетическая запись, маршрут, метод и схема ответа во владении теста.
  • Изменение: одно контролируемое условие, например статус 503, пустой список или ограниченная задержка.
  • Наблюдение: видимое состояние и короткий счётчик совпадений маршрута.
  • Очистка: удаление маршрута, закрытие контекста и статус хранения артефакта.

Одно изменение делает сбой понятным. Если fixture одновременно меняет аутентификацию, retries, cache и данные базы, успешная проверка не показывает, какой контракт был испытан. Версионируйте схему рядом с тестом и используйте значения, которые нельзя принять за запись клиента. Fixture может воспроизводить известный инцидент upstream, но имя должно явно указывать на синтетический характер.

Выберите минимальный сетевой API

Матрица связывает один вопрос теста с поверхностью перехвата и границей доказательства. Используйте отдельную строку для каждого контракта, а не обработчик для всего трафика.

ВопросPlaywrightSelenium/WebDriverГраница доказательства
Вернуть известный JSONpage.route('**/api/items', route => route.fulfill({ json }))Тестовый proxy или собственный stub endpoint до запуска driverОтрисованное состояние для синтетического ответа
Проверить ошибку сервераroute.fulfill({ status: 503, body: ... })Stub endpoint с указанным статусомИнтерфейс ошибки и восстановление, не здоровье сервиса
Смоделировать задержкуroute.fulfill({ delay: 250, ... }) или управляемый stubProxy или stub задерживает только названный маршрутПереход загрузки и timeout
Наблюдать без изменения трафикаroute.continue() и редактированный счётчикЛог proxy только с метаданными запросаПуть запроса, не успешная авторизация
Проверить завершениеpage.unroute() в finally и закрытие контекстаОстановить собственный proxy и завершить driverНет утечки обработчика или состояния worker

Не используйте **/*, если тест специально не проверяет сетевую политику. Широкий обработчик может скрыть отсутствующий ресурс и сделать зелёный результат не связанным с реальным сценарием. Для одного маршрута проверяйте метод, возвращайте небольшой fixture, а остальные запросы продолжайте или отклоняйте согласно контракту. Неожиданный запрос должен давать явную ошибку, а не выдуманный ответ.

Сохраните воспроизводимый failure fixture

Fixture меняет одно условие и называет ожидаемый результат для пользователя. Пример Playwright возвращает синтетическую аварию, сохраняет первую ошибку assertion и всегда удаляет обработчик вместе с контекстом:

const context = await browser.newContext();
const page = await context.newPage();
let matched = 0;
await page.route('**/api/items', async route => {
  matched += 1;
  await route.fulfill({
    status: 503,
    contentType: 'application/json',
    body: JSON.stringify({ code: 'owned-test-outage' }),
  });
});
try {
  await page.goto('http://test.local/items');
  await expect(page.getByRole('alert')).toHaveText('Items are temporarily unavailable');
  expect(matched).toBe(1);
} finally {
  await page.unroute('**/api/items');
  await context.close();
}

Fixture доказывает, что страница показывает документированную ошибку и путь восстановления для такого ответа. Он не доказывает, что реальный upstream отдаёт 503, что повтор безопасен, что авторизация прошла или что удалённая запись не изменилась. Храните синтетический код и схему рядом с тестом. Не переносите секреты production в fixture.

Если могут завершиться ошибкой настройка, проверка и очистка, сохраните первую ошибку и вынесите очистку в отдельное поле. Поздняя ошибка закрытия не должна скрывать исходную причину. Повтор создаёт новый контекст и новую попытку; последующий успех не доказывает отсутствие эффекта первой запроса.

Решите, когда нужна реальность

Мокирование является ограниченным компромиссом:

Цель тестаМокировать?Почему
Проверить загрузку, пустое состояние, валидацию или аварию собственной страницыДаДетерминированный вход повторяет контракт UI
Проверить сериализацию и поддерживаемый клиент-серверный контрактОбычно нет, используйте собственную интеграциюMock не обнаруживает drift схемы или транспорта
Проверить платёж, личность или необратимую операциюНет для финального утвержденияТолько разрешённый сервис устанавливает удалённый результат
Воспроизвести известный инцидент upstreamДа, с именованным fixtureУсловие явно и доступно ревью
Исследовать или менять сторонний сервисНетВне владения и разрешений теста

Сочетайте частые mocked-тесты с небольшим числом одобренных реальных интеграций. Mocked-тест запускается при каждом изменении, интеграция проверяет согласованность маршрута, заголовков, схемы, авторизации и политики сервиса. Разделяйте имена и свидетельства. Видимый в браузере ответ не доказывает платёж, изменение аккаунта, удаление данных или доступность сервиса без свидетельства его владельца.

Изолируйте контексты, данные и артефакты

Создавайте новый BrowserContext, когда cookies, хранилище, права, кэш или браузерный worker могут повлиять на маршрут. Каждому исполнителю нужен собственный каталог артефактов и синтетические идентификаторы. Контекст изолирует состояние браузера, но не общую базу, очередь или proxy-процесс. Используйте независимые записи или сериализуйте мутацию через поддерживаемую операцию приложения.

Файлы fixture и метки маршрутов являются входами теста. Эталон только для чтения можно скопировать в каталог исполнителя, но частичный результат не должен заменять эталон, который читает другой исполнитель. Храните отдельно снимки, трассировки и счётчики, соблюдая политику хранения. Короткая квитанция включает сценарий, совпавший маршрут, класс ответа, видимый результат и состояние очистки.

Очистка должна быть в finally: удалите маршрут, закройте страницы и контексты, остановите собственный proxy и сообщите об ошибке рядом с проверкой. Если браузерный worker или кэш отдаёт другой ответ, запишите путь и осознанно измените подготовку fixture. Не расширяйте перехват ради совпадения.

Разделяйте production и тестовый трафик

Используйте тестовый hostname или поддерживаемый приложением режим, синтетические identities и отдельное хранилище данных. Переключение на mocking должно быть явно видно в runner и CI. Production-сборка не должна импортировать тестовые handlers, а smoke-проверка production должна завершаться fail-closed при наличии тестовой опции. Это граница поставки, а не соглашение об именах.

Для реальной интеграции используйте одобренную владельцем сервиса учётную запись и его политику хранения. Не выдавайте квитанцию мока за доказательство интеграции. Сопоставьте пользовательский контракт и подпишите факты браузера отдельно от фактов сервиса. Если настоящий маршрут недоступен, запишите внешнее условие, а не заменяйте его синтетическим успехом.

Возможности и ограничения BotBrowser

BotBrowser предоставляет изолированные BrowserContext для повторяемого разрешённого сценария с отдельными cookies, хранилищем и правами, но не проверяет удалённую базу и не гарантирует эффект сервиса.

BotBrowser может предоставить изолированный BrowserContext с отдельным состоянием браузера для разрешённого теста. Это полезно, когда сетевой fixture должен начинать с известных cookies, хранилища, прав или чистого состояния браузерного worker. BotBrowser не решает, какие маршруты разрешено перехватывать, не делает трафик третьей стороны собственностью теста и не проверяет базу, биллинг, авторизацию или удалённые побочные эффекты. Владелец теста предоставляет синтетические данные, управляет обработчиком, закрывает контекст и запрашивает свидетельства приложения за пределами браузерной границы.

Храните возможность и ограничение рядом в отчёте. Записывайте назначение контекста, версии браузера и framework, маршрут, класс ответа, видимый результат и квитанцию cleanup. Укажите, что зависимость была синтетической. Mocked pass не доказывает production availability, authorization или успешную бизнес-транзакцию. Чистый контекст также не доказывает отзыв серверной сессии или очистку удалённой очереди.

Проверьте границу перед слиянием

Убедитесь, что обработчик соответствует одному собственному маршруту, fixture меняет одно условие, неперехваченный трафик следует намеренной политике, а очистка выполняется после ошибок настройки и проверки. Запустите синтетический успех, принудительный сбой и одобренную реальную интеграцию, если этого требует контракт. Проверяйте свежий результат по метке маршрута и квитанции, а не по полному телу запроса.

Явно подтвердите четыре отрицательных утверждения: fixture не меняет сторонний сервис; ответ не доказывает здоровье upstream; изоляция браузера не удаляет данные сервера; retry не стирает неопределённость первой попытки. Так сетевые свидетельства остаются полезными, частными и соразмерными тестируемому поведению.

Сначала определите владельца маршрута. URL приложения может принадлежать поставщику или другой команде. Запишите владельца и одобренную среду. Если владелец неизвестен, оставьте запрос в разрешённой интеграции и проверяйте только разрешённое поведение.

Аутентификация требует той же границы. Используйте синтетические сессии, явно задавайте разрешённые роли и не копируйте сессию клиента. Mock 401 или 403 проверяет интерфейс, но не доказывает решение реального policy engine.

Выберите явную политику кэша. Очистка делает fixture стабильным, а сохранение проверяет кэшированный ответ. Запишите выбор и совпадение маршрута. Если кэш ответил раньше handler, сохраните наблюдение и не расширяйте шаблон.

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

Сделайте совпадение явным: метод HTTP, путь и параметры сценария. Маршрут для всех методов может превратить запись в синтетический ответ. Правило без версии клиента способно скрыть обновление. Небольшие условия показывают неожиданный запрос.

Класс ответа является частью контракта. Успех содержит нужные странице поля, ошибка следует документированной форме. Лишние поля скрывают случайную зависимость, пропущенные создают недокументированный контракт.

Сделайте время наблюдаемым без хрупкости. Ограниченная задержка проверяет загрузку, но assertion ждёт переход состояния, а не фиксированный sleep. Укажите задержку в fixture и отличайте ожидаемый timeout от отсутствия совпадения.

Используйте один receipt для mocked и real-проверок: сценарий, маршрут, метод, класс, состояние, счётчик и cleanup. Не помещайте учетные данные, cookies, полные тела или закрытый текст. Так не путаются наблюдение браузера и факт сервиса.

Retry требует отдельного assertion. Каждая попытка новая и сохраняет исходный результат. Если первая могла достичь реального сервиса, отметьте неопределённость. Mock проверяет повтор, а эффект подтверждает только одобренная интеграция.

Держите fixture рядом с объясняющей assertion. Назовите синтетическое условие, свяжите видимый контракт и укажите реальную проверку транспорта и сервиса.

Аутентификация также требует явной границы. Используйте синтетические сессии, задавайте разрешённые роли и не копируйте сессии клиентов. Mock 401 или 403 проверяет интерфейс, но не доказывает реальное решение.

Выберите политику кэша. Очистка стабилизирует fixture, сохранение проверяет кэшированный ответ. Запишите выбор и сохраните наблюдение, если кэш ответил раньше handler.

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

Источники

#Браузерная Автоматизация#Сетевое Мокирование#Playwright#Тестовые Двойники#Детерминированные Тесты

Переведите BotBrowser из исследований в продакшн

Используйте эти руководства, чтобы понять модель, а затем перейти к кроссплатформенной валидации, изолированным контекстам и масштабируемому браузерному развертыванию.