Принципы написания поддерживаемых тестов

Чистая структура теста

Поддерживаемый тест начинается с четкой структуры. В Playwright тест обычно разделяется на три части:

  1. Arrange (Подготовка) — настройка данных, переход на нужную страницу, инициализация объектов.
  2. Act (Действие) — выполнение действий пользователя: клики, ввод текста, навигация.
  3. Assert (Проверка) — проверка состояния приложения: наличие элементов, значения полей, URL, данные API.

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


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

Правильный выбор селекторов — основа надежности теста. Рекомендуется использовать:

  • data-testid / data-test — специальные атрибуты для тестирования, которые не меняются при редизайне интерфейса.
  • Текст элементов — только если текст стабильный и уникальный.
  • CSS-классы — только для базовой стилизации, изменения классов могут сломать тесты.
  • XPath — использовать крайне редко, только когда других вариантов нет.

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


Асинхронность и ожидания

Playwright работает с асинхронными действиями, поэтому правильное ожидание состояния элементов критично. Основные подходы:

  • await page.waitForSelector(selector, { state: 'visible' }) — дождаться, пока элемент появится.
  • await elementHandle.isVisible() — проверка видимости элемента.
  • await expect(page.locator(selector)).toHaveText('Текст') — встроенные проверки с ожиданием.

Жесткие таймеры (setTimeout, wait) следует избегать, они делают тесты медленными и ненадежными. Playwright имеет встроенные механизмы ожидания элементов и действий, которые лучше использовать.


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

Для поддержки большого количества тестов важно:

  • Создавать помощники и утилиты для часто повторяющихся действий, например, логин, заполнение форм.
  • Использовать Page Object Model (POM) — отдельные классы или модули для каждой страницы. Это позволяет менять селекторы и логику в одном месте без переписывания всех тестов.

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

class LoginPage {
    constructor(page) {
        this.page = page;
        this.usernameInput = page.locator('[data-testid="username"]');
        this.passwordInput = page.locator('[data-testid="password"]');
        this.submitButton = page.locator('[data-testid="login-button"]');
    }

    async login(username, password) {
        await this.usernameInput.fill(username);
        await this.passwordInput.fill(password);
        await this.submitButton.click();
    }
}

Управление состоянием и изоляция тестов

Поддерживаемые тесты должны быть независимыми друг от друга:

  • Каждый тест должен создавать собственное состояние, например, через API или фикстуры.
  • Нельзя полагаться на результат предыдущего теста.
  • Использование beforeEach и afterEach позволяет гарантировать чистое окружение:
test.beforeEach(async ({ page }) => {
    await page.goto('/login');
});

test.afterEach(async ({ page }) => {
    await page.context().clearCookies();
});

Читаемость и именование

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

  • Плохо: test('Клик на кнопку')
  • Хорошо: test('После клика на кнопку "Сохранить" появляется сообщение об успешном сохранении')

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


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

Для сложных сценариев важно добавлять логирование и скриншоты:

  • await page.screenshot({ path: 'screenshot.png' }) — полезно при падении теста.
  • console.log() можно использовать для вывода состояния переменных, но следует ограничивать его в финальной версии.

Playwright поддерживает видео- и лог-запись тестов, что облегчает анализ нестабильных или редких ошибок.


Параметризация и повторное использование данных

Для проверки одного сценария с разными данными:

const users = [
    { username: 'admin', password: 'admin123' },
    { username: 'guest', password: 'guest123' },
];

users.forEach(user => {
    test(`Логин пользователя ${user.username}`, async ({ page }) => {
        await loginPage.login(user.username, user.password);
        await expect(page).toHaveURL('/dashboard');
    });
});

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


Работа с сетевыми запросами

Playwright позволяет перехватывать и мокать запросы:

  • page.route — для перехвата и изменения ответа сервера.
  • page.waitForResponse — ожидание определенного ответа API.

Правильное управление сетевыми данными делает тесты устойчивыми к изменениям на сервере и позволяет проверять поведение приложения в изолированной среде.


Управление временем выполнения тестов

Поддерживаемые тесты должны быть быстрыми и надежными:

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

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


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