Поддержка и развитие test suite

Тестовый набор в Playwright строится вокруг логики модульности и переиспользуемости. Основное правило — каждая тестовая функция должна быть независимой, чтобы её можно было запускать отдельно или параллельно с другими. Для этого используются:

  • test.describe — группировка тестов по функционалу или странице приложения.
  • test.beforeEach / test.afterEach — установка и очистка состояния перед и после каждого теста.
  • fixtures — создание повторно используемых объектов, таких как авторизация, подготовка данных или браузерные контексты.

Пример структуры:

import { test, expect } from '@playwright/test';

test.describe('Страница логина', () => {
  test.beforeEach(async ({ page }) => {
    await page.goto('/login');
  });

  test('Успешная авторизация', async ({ page }) => {
    await page.fill('#username', 'user1');
    await page.fill('#password', 'password123');
    await page.click('button[type="submit"]');
    await expect(page).toHaveURL('/dashboard');
  });

  test('Ошибка при неверном пароле', async ({ page }) => {
    await page.fill('#username', 'user1');
    await page.fill('#password', 'wrongpassword');
    await page.click('button[type="submit"]');
    await expect(page.locator('.error')).toBeVisible();
  });
});

Управление конфигурацией и окружениями

Playwright позволяет разделять конфигурации для разных окружений через файл playwright.config.js. Ключевые моменты:

  • Определение базового URL: baseURL.
  • Настройка браузеров: chromium, firefox, webkit.
  • Параметры параллельного запуска: workers.
  • Управление временными задержками и таймаутами: timeout, expect.timeout.

Пример конфигурации:

import { defineConfig } from '@playwright/test';

export default defineConfig({
  testDir: './tests',
  timeout: 30000,
  retries: 1,
  use: {
    headless: true,
    baseURL: 'https://example.com',
    viewport: { width: 1280, height: 720 },
    actionTimeout: 10000,
  },
  projects: [
    { name: 'chromium', use: { browserName: 'chromium' } },
    { name: 'firefox', use: { browserName: 'firefox' } },
  ],
});

Разделение проектов позволяет запускать один и тот же тест на разных браузерах без дублирования кода.


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

Стабильность достигается сочетанием ожиданий элементов, синхронизации с DOM и обработки асинхронных операций. Основные приёмы:

  1. Явные ожидания через expect:
await expect(page.locator('button#submit')).toBeEnabled();
await expect(page).toHaveURL(/dashboard/);
  1. Локаторы вместо прямых селекторов:
const loginButton = page.getByRole('button', { name: 'Войти' });
await loginButton.click();

Использование getByRole, getByLabel и других методов повышает читабельность и устойчивость тестов к изменениям UI.

  1. Обработка асинхронности:
await page.waitForResponse(resp => resp.url().includes('/api/login') && resp.status() === 200);

Параллельный и последовательный запуск

Playwright поддерживает масштабирование тестов:

  • Параллельный запуск (workers > 1) ускоряет выполнение, но требует, чтобы тесты были изолированы и не зависели от общего состояния.
  • Последовательный запуск (test.serial) используется для сценариев, где порядок действий критичен, например при сложных сценариях e2e.

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

test.describe.serial('Сценарий корзины', () => {
  test('Добавление товара', async ({ page }) => { /* ... */ });
  test('Оформление заказа', async ({ page }) => { /* ... */ });
});

Логирование и отладка

Эффективная поддержка тестового набора невозможна без подробного логирования и скриншотов:

  • page.screenshot({ path: 'screenshots/test.png' }) для фиксации состояния страницы.
  • page.video().saveAs() для записи видео выполнения.
  • trace.start() и trace.stop() для создания трассировки, которая позволяет просматривать шаги теста в Playwright Trace Viewer.

Эти инструменты помогают быстро находить причины падений и выявлять нестабильные места тестового набора.


Поддержка тестов и рефакторинг

С ростом приложения тестовый набор нуждается в постоянной реорганизации:

  • Вынесение повторяющихся действий в helper-функции или page objects:
class LoginPage {
  constructor(page) { this.page = page; }
  async login(username, password) {
    await this.page.fill('#username', username);
    await this.page.fill('#password', password);
    await this.page.click('button[type="submit"]');
  }
}
  • Использование fixtures для глобальных объектов, например авторизации или данных пользователя.
  • Регулярная проверка на дублирование кода и удаление устаревших тестов.
  • Настройка CI/CD интеграции, чтобы тесты запускались автоматически при коммите или PR.

Отслеживание стабильности тестов

Важный аспект — метрики стабильности:

  • retries для автоматического повторного запуска падающих тестов.
  • Логи падений, видео и скриншоты для анализа проблем.
  • Разделение критических и второстепенных тестов: критические должны падать при малейшей проблеме, второстепенные — предупреждать, но не блокировать релиз.

Работа с данными и состояниями

Для повторяемости тестов необходимо чётко контролировать состояние приложения и данные:

  • Очистка базы перед тестом или использование моков API.
  • Изоляция данных для параллельного запуска.
  • Генерация уникальных значений (например, email) для предотвращения конфликтов.
const randomEmail = `user_${Date.now()}@example.com`;
await page.fill('#email', randomEmail);

Такой подход делает тесты предсказуемыми и легко поддерживаемыми при масштабировании.