Большие тестовые наборы в Playwright выполняются медленно, стопорят CI-конвейер и задерживают обратную связь. В этом руководстве предложены практики, позволяющие определить и устранить распространенные узкие места.
Проблемные места в Playwright
Задержки при запуске браузера:
- Проблема: Запуск нового экземпляра браузера для каждого теста, при параллельном выполнении нескольких тестов.
- Последствия: Ожидание запуска всех экземпляров в больших тестовых наборах.
Время загрузки страницы:
- Проблема: Ожидание полной загрузки страниц, включая все связанные с ними ресурсы (CSS, JavaScript, изображения, шрифты), является основным фактором, влияющим на продолжительность теста.
- Последствия: Сложные страницы с большим количеством контента (с большим количеством сетевых запросов) могут значительно задержать выполнение теста.
Сетевые запросы:
- Проблема: Слишком много сетевых запросов — скрипты, изображения, шрифты и другие ресурсы могут увеличить общую продолжительность набора.
- Последствия: Сторонние скрипты, трекеры аналитики и рекламные сети могут создавать дополнительные задержки.
Неоптимальные ожидания:
- Проблема: Чрезмерное использование явных (эксплицитных) ожиданий (
page.waitForTimeout()
) часто указывает на неэффективные селекторы или неправильную логику в ваших тестах. Неявные (имплицитные) ожидания также могут вызывать задержки, если они не настроены должным образом. - Последствия: Неоптимальные ожидания могут значительно увеличить время выполнения теста, что приводит к заметным задержкам в получении результатов тестирования.
Неправильная структура тестов:
- Проблема: Плохо структурированные тесты с избыточными действиями, неэффективными ассертами или длительными процедурами установки/настройки и завершения могут значительно увеличивать время выполнения набора.
- Последствия: Недостатки структуры, хотя и кажутся незначительными для каждого отдельного теста, могут приводить к значительной суммарной задержке при масштабировании на большой тестовый набор.
Стратегии ускорения
Используйте параллелизм:
- Уровень тестов: Одновременный запуск нескольких тестов в рамках одного worker-процесса, что повышает скорость (если в тестах нет сильных зависимостей):
test.concurrent('should login successfully', async ({ page }) => { ... }); test.concurrent('should display error for invalid credentials', async ({ page }) => { ... });
- Уровень worker’ов: Распределите тесты между несколькими процессами, чтобы задействовать все ядра процессора:
// playwright.config.js export default defineConfig({ workers: 4, // Use 4 worker processes (adjust to your machine's capabilities) });
- Шардинг: Разделите набор тестов на мелкие фрагменты (шарды) и запускайте их отдельно на машинах или CI-узлах:
npx playwright test --shard=1/3 # Run the first shard
По теме: Туториал по шардингу в Playwright
- Облачное распараллеливание: Для достижения максимальной скорости используйте облачные сервисы, такие как BrowserStack или LambdaTest, чтобы запускать тесты одновременно в сотнях окружений:
// playwright.config.js (example for BrowserStack) export default defineConfig({ use: { browserStack: { username: process.env.BROWSERSTACK_USERNAME, accessKey: process.env.BROWSERSTACK_ACCESS_KEY, }, }, projects: [ { name: 'chrome', use: { browserName: 'chromium' } }, { name: 'firefox', use: { browserName: 'firefox' } }, // ... more browser configurations ], });
Основные селекторы и ожидания:
- CSS-селекторы: Точнее таргетируйте элементы, с помощью уникальных селекторов или атрибутов данных:
await page.click('button[data-testid="submit-button"]'); // Best practice await page.click('text=Submit'); // Alternative using text content
- Эксплицитные ожидания: Замените стандартный
waitForTimeout
на более точныйwaitForSelector
илиwaitForNavigation
:
await page.waitForSelector('.success-message'); await page.waitForNavigation();
- Умные ожидания: Используйте
waitForFunction
для ожидания определенных условий на странице:
await page.waitForFunction(() => document.readyState === 'complete');
Кэширование и повторное использование:
- Контексты браузера: Повторное использование одного и того же контекста браузера для нескольких тестов в одном файле:
// Create the context ONCE at the top level of the file const context = await browser.newContext(); test('test1', async ({ page }) => { // Use the same context for all tests in this file await page.goto('https://example.com'); // ... }); test('test2', async ({ page }) => { // ... });
- Аутентификация: Сохраняйте и повторно используйте статусы и артефакты аутентификации (например, cookie-файлы), чтобы избежать лишних логинов:
const storageState = await context.storageState(); fs.writeFileSync('auth.json', JSON.stringify(storageState)); // ... later in another test ... const context = await browser.newContext({ storageState: JSON.parse(fs.readFileSync('auth.json')) });
- Codegen: Записывайте пользовательские потоки для автоматической генерации тестового кода во встроенном генераторе Playwright.
Оптимизация запросов:
- Перехват: Имитация или блокировка проблемных сетевых запросов:
await page.route('**/api/**', route => route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ data: 'mocked data' }), }));
Корректировка тестов:
- Независимость: Создавайте изолированные тесты, избегая общих статусов для нескольких, которые могут привести к непредвиденным взаимодействиям.
- Приоритезируйте: Выполняйте сначала более важные тесты, чтобы быстрее получить обратную связь по основной функциональности.
- Устраняйте всё лишнее: Проанализируйте набор, чтобы убрать ненужные действия и ассерты.
Оборудование и инфраструктура:
- Больше ядер: Инвестируйте в мощные машины с большим количеством процессорных ядер, чтобы максимизировать преимущества распараллеливания.
- Оперативная память: Обеспечьте достаточный объем памяти.
- Облачная инфраструктура: Рассмотрите возможность использования провайдеров облачного тестирования, чтобы масштабировать проблемные тесты в облаке у провайдера.
Профилирование и анализ:
- Трассировщик: Используйте встроенный в Playwright просмотр трассировки, чтобы получить представление о примерном времени выполнения тестов. Это позволит выявить особо медленные шаги, проблемные места в целом, и возможности для оптимизации.
- Инструменты профилирования производительности: Используйте такие инструменты, как Lighthouse или Chrome DevTools, для анализа производительности веб-приложения. Решение общих проблем с производительностью приложения может косвенно привести к ускорению тестов.
Продвинутые методы
Тонкая настройка стратегий загрузки страниц:
- Navigation Timing API: Используйте Navigation Timing API для точного измерения событий загрузки страницы и поиска узких мест. Возможно понадобится точнее прописать ожидания — на основе определенных метрик, таких как
loadEventEnd
илиDOMContentLoaded
:
await page.evaluate(() => performance.timing.loadEventEnd);
- Обнаружение простоя сети: Вместо того чтобы ждать загрузки всей страницы, попробуйте ориентироваться на неактивность сети. Это может быть эффективно при работе со страницами, которые загружают динамическое содержимое асинхронно:
await page.waitForLoadState('networkidle');
- Ленивая загрузка и обнаружение пересечений: Если ваше приложение использует ленивую загрузку (Lazy Loading) для изображений или контента, расположенного ниже активного экрана, эксплицитно запустите загрузку этих элементов перед взаимодействием с ними. Для этого может пригодиться Intersection Observer API.
Настроенные ожидания с помощью jQuery (или других библиотек):
- Ожидание динамического контента: Если ваше приложение в значительной степени полагается на JavaScript для отображения контента, используйте jQuery (или аналогичные библиотеки) для ожидания появления определенных элементов или выполнения условий:
await page.waitForFunction(() => $('#dynamic-elements').length > 0);
- Видимость элементов: Убедитесь, что элементы не только присутствуют в DOM, но и видны перед взаимодействием с ними. Селектор
jQuery:visible
можно использовать для создания более надежных ожиданий:
await page.waitForSelector('#visibled');
Советы по оптимизации сети:
- Моки и стабы: Для тестов, которые сильно зависят от внешних API-интерфейсов, рассмотрите возможность создания заглушек-моков или стабов, чтобы избежать задержек, связанных с реальными сетевыми запросами. Для этого можно использовать такие библиотеки, как nock или msw (Mock Service Worker).
- Service Workers: Обратите внимание на то, как работники сервисов кэшируют данные, если ваше приложение использует их. При проведении тестов, требующих новых данных, очищайте или обходите кэши service worker’ов.
- Троттлинг ресурсов: Чтобы найти узкие места в производительности тестов и убедиться, что ваше приложение более-менее справляется с нагрузкой, имитируйте медленное подключение в Playwright.
Трассировщик Playwright (Trace Viewer):
- Визуализация выполнения теста: В средстве просмотра трассировки можно увидеть временную шкалу сетевых запросов, событий навигации по странице и выполнения JavaScript. Используйте ее, чтобы определить, какие части ваших тестов занимают больше всего времени.
- Выявление нестабильных тестов: Поищите в трассировщике длительные паузы или некорректные сетевые запросы.
Резюме
Поддержание максимальной скорости и эффективности тестирования требует постоянного мониторинга и совершенствования. Оптимизация производительности — это постоянная работа.