Изоляция нестабильных тестов

Нестабильные тесты — это те сценарии, которые иногда проходят успешно, а иногда завершаются сбоем без изменений в коде приложения. Их наличие снижает доверие к тестовому набору и усложняет выявление реальных дефектов. В Playwright существует несколько методов для минимизации нестабильности и обеспечения изоляции тестов.


Причины нестабильности тестов

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

  2. Асинхронные операции и тайминги Ошибки могут возникать из-за задержек сети, медленной загрузки элементов или неправильного ожидания элементов.

  3. Конкуренция за ресурсы Запуск нескольких тестов на одном браузере или сервере может вызывать конфликты, если они используют одни и те же данные или ресурсы.

  4. Влияние внешних сервисов Интеграционные тесты, обращающиеся к внешним API или базам данных, могут ломаться из-за сетевых сбоев или изменений данных.


Стратегии изоляции тестов

Использование отдельного браузерного контекста

Playwright позволяет создавать отдельный контекст браузера для каждого теста:

const { test, chromium } = require('@playwright/test');

test('тест с изолированным контекстом', async ({}) => {
  const browser = await chromium.launch();
  const context = await browser.newContext(); // отдельный контекст
  const page = await context.newPage();

  await page.goto('https://example.com');
  // операции с страницей

  await context.close();
  await browser.close();
});

Преимущества:

  • Нет перекрёстного влияния данных между тестами.
  • Изоляция куки, локального хранилища и сессий.

Очистка состояния перед тестом

Для гарантированной чистоты среды можно очищать куки и локальное хранилище:

test.beforeEach(async ({ page }) => {
  await page.context().clearCookies();
  await page.evaluate(() => localStorage.clear());
});

Ключевой момент: это обеспечивает предсказуемое состояние страницы перед каждым тестом.


Использование фикстур Playwright

Фикстуры (fixtures) позволяют настраивать окружение для каждого теста, обеспечивая изоляцию:

const { test } = require('@playwright/test');

test.use({
  viewport: { width: 1280, height: 720 },
  storageState: null, // отсутствие сохранённого состояния
});

test('тест с фикстурами', async ({ page }) => {
  await page.goto('https://example.com');
});

Фикстуры могут быть глобальными или локальными для отдельных тестов, что уменьшает риск побочных эффектов.


Параллельный запуск и изоляция

Playwright поддерживает параллельное выполнение тестов. Для безопасного запуска:

  • Каждый тест должен иметь собственный контекст.
  • Использовать отдельные данные (например, уникальные пользователи или записи в базе).
  • Не обращаться к общим ресурсам без синхронизации.
// playwright.config.js
module.exports = {
  workers: 4, // количество параллельных потоков
};

Параллельность ускоряет тесты, но требует строгой изоляции ресурсов.


Ожидания и синхронизация

Нестабильность часто вызвана неправильной синхронизацией:

await page.waitForSelector('#element', { state: 'visible', timeout: 5000 });
await page.click('#element');

Советы:

  • Использовать state: 'visible' или state: 'attached' вместо простого ожидания.
  • Избегать жёстких sleep, предпочитая ожидания состояния элементов.

Моки и стабы

Для уменьшения зависимости от внешних сервисов используют мок-серверы и стабы:

await page.route('**/api/data', route =>
  route.fulfill({
    status: 200,
    body: JSON.stringify({ key: 'value' }),
  })
);

Преимущества:

  • Полная предсказуемость данных.
  • Исключение ошибок из-за недоступности API.
  • Возможность тестировать крайние случаи ответа сервера.

Локализация нестабильных тестов

Если тест всё равно нестабилен, его можно временно пометить:

test.describe.skip('нестабильные тесты', () => {
  test('иногда падает', async ({ page }) => {
    await page.goto('https://example.com');
  });
});

Или использовать test.fail() с комментарием для фиксации причины падения. Это помогает не блокировать весь набор тестов.


Практические рекомендации

  1. Каждый тест — независимый блок. Ни один тест не должен полагаться на состояние предыдущего.
  2. Минимизировать прямое использование глобальных данных или сетевых ресурсов.
  3. Использовать фикстуры и отдельные контексты для каждого теста.
  4. Проверять элементы с помощью надежных ожиданий (waitForSelector, waitForResponse).
  5. Мокировать внешние зависимости для стабильных и повторяемых результатов.
  6. Постепенно рефакторить и локализовать нестабильные тесты, вместо массового исключения из набора.

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