E2E-тесты и CI с Playwright и GitHub Actions
Представьте интернет-магазин. Перед релизом нужно убедиться, что гость может зарегистрироваться, положить товар в корзину и оплатить заказ. Ручная проверка — открыть сайт в браузере и пройти сценарий пальцами. E2E-тест (end-to-end, "от начала до конца") делает то же самое программой: скрипт управляет браузером, кликает по кнопкам и сверяет результат.
Playwright — библиотека от Microsoft для такой автоматизации. Она умеет Chromium, Firefox и WebKit (движок Safari). GitHub Actions — встроенный в GitHub сервис CI: на каждый push или pull request он может собрать проект, прогнать тесты и сохранить отчёт.
Базовые темы — DevOps и CI/CD, тестирование в проекте, Git.
Словарь перед стартом
| Термин | Простыми словами |
|---|---|
| CI (Continuous Integration) | Код из ветки автоматически собирают и проверяют при каждом изменении. Ошибка видна до слияния в main. |
| CD (Continuous Delivery / Deployment) | После зелёных проверок артефакт готов к выкладке на staging или production (часто с ручным подтверждением). |
| Pipeline | Цепочка шагов в CI: checkout → установка зависимостей → тесты → деплой. |
| Workflow | Файл с описанием pipeline в GitHub Actions (YAML в .github/workflows/). |
| Runner | Виртуальная машина, где выполняются шаги workflow. |
| E2E | Проверка всей цепочки "браузер → фронтенд → API → база" одним сценарием. |
| Flaky test | Тест, который то проходит, то падает без изменения кода (тайминги, сеть, общие данные). |
| Staging | Копия продакшена для проверок: те же сервисы, тестовые данные и ключи. |
CI отвечает на вопрос: "Сломали ли мы проект этим коммитом?" E2E в CI добавляет: "Может ли пользователь пройти критичный путь в браузере?"
Чем E2E отличается от других тестов
Тесты делят по глубине и скорости. Удобная модель — пирамида тестирования: внизу много быстрых unit-тестов, наверху — немного медленных E2E.
| Уровень | Что проверяет | Скорость | Типичный пример |
|---|---|---|---|
| Unit | Одна функция или класс в изоляции | Секунды, тысячи за минуту | calculateDiscount(100, 0.1) === 90 |
| Integration | Связка модулей: API + БД, очередь + воркер | Минуты | POST /orders создаёт строку в PostgreSQL |
| E2E | Поведение через UI, как у пользователя | Минуты на сценарий | Логин → корзина → оплата → экран "Спасибо" |
Unit ловит ошибку в формуле скидки. E2E ловит ситуацию, когда кнопка "Оплатить" серая из-за бага в вёрстке, CORS блокирует запрос или API возвращает 500 только после полного сценария.
E2E дорогие — нужен браузер, поднятое окружение, стабильные тестовые данные. Поэтому в CI обычно держат 5–15 критичных сценариев, а детали кнопок и валидации полей покрывают unit- и integration-тестами.
Когда E2E оправданы
Подходят сценарии, где цена ошибки высока и путь проходит через UI:
- вход и восстановление пароля;
- оформление заказа или подписки;
- создание документа, заявки, тикета в поддержку;
- смена роли или прав доступа в админке.
Слабое место E2E — хрупкость к селекторам — тест цепляется за div > div > button:nth-child(3), дизайнер меняет вёрстку — тест красный, продукт для пользователя жив. Решение: стабильные атрибуты (data-testid) и роли ARIA (getByRole('button', { name: 'Оплатить' })).
Playwright — как устроен тест
Playwright запускает реальный браузер (headless — без окна на экране, или headed — с окном при отладке). Основные объекты:
page— вкладка браузера — переходы, клики, ввод текста.locator— "указатель" на элемент (кнопка, поле). Playwright ждёт, пока элемент станет кликабельным.expect— проверка — заголовок, текст, URL, видимость элемента. Встроены повторные попытки (retry), меньше ручныхsleep.
Минимальный пример:
import { test, expect } from '@playwright/test';
test('главная открывается и виден заголовок', async ({ page }) => {
await page.goto('https://example.com');
await expect(page).toHaveTitle(/Example Domain/);
await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
});
Что происходит по шагам:
test(...)— объявление одного сценария; имя попадёт в отчёт CI.page.goto— открыть URL и дождаться загрузки (по умолчанию — событиеload).expect(page).toHaveTitle— заголовок вкладки совпадает с регулярным выражением.getByRole('heading')— поиск по доступности (как озвучивает screen reader).
Селекторы — что выбирать
| Способ | Когда использовать |
|---|---|
getByRole, getByLabel | Кнопки, ссылки, поля с подписью — ближе к тому, как видит пользователь |
getByTestId('checkout-submit') | Явный договор с фронтендом: атрибут только для тестов |
| CSS/XPath по глубокой вложенности | Крайний случай: ломается при любом рефакторинге UI |
Автоожидание и "flaky"
В старых инструментах часто писали Thread.sleep(3000) "на всякий случай". Playwright сам ждёт — элемент появился, сеть успокоилась, анимация закончилась. Проверки через expect повторяются, пока условие истинно или не истечёт таймаут.
Если тест всё равно нестабилен — причина обычно в данных (два теста бьют в одного пользователя), окружении (staging лежит) или внешних сервисах (платёжный sandbox недоступен).
Параллельный запуск и отчёты
В CI тесты делят на шарды (части набора):
shard 1/4;2/4… — четыре runner’а прогоняют четверть сценариев каждый;- время падает почти в четыре раза.
При падении Playwright сохраняет trace (скриншоты, сеть, DOM на каждом шаге), screenshot и video — в GitHub Actions их кладут в artifacts, чтобы разобрать падение без локального воспроизведения.
Playwright одинаково подходит для SPA (React, Vue, Angular) и SSR (Next.js, классический серверный HTML): важно дождаться нужного состояния страницы, а не только первого load.
Локальный запуск (с чего начать)
npm init playwright@latest # шаблон проекта, конфиг, примеры
npx playwright test # прогон всех тестов
npx playwright test --ui # интерактивный режим
npx playwright codegen URL # запись действий в код
Команда codegen полезна новичкам: вы кликаете в браузере, Playwright генерирует черновик селекторов — потом его правят на getByRole / data-testid.
GitHub Actions — workflow для E2E
GitHub Actions реагирует на события репозитория — push, pull_request, расписание schedule, ручной workflow_dispatch.
Структура YAML:
on— когда запускать.jobs— логические блоки (могут идти параллельно).steps— команды внутри job — checkout кода,npm ci,playwright test.
Типичный файл .github/workflows/e2e.yml:
Код ITЗагрузка примера кода…
Разбор по строкам:
runs-on: ubuntu-latest— свежий Linux-образ на GitHub-hosted runner.npm ci— чистая установка поpackage-lock.json(повторяемо в CI).playwright install --with-deps— браузеры и системные библиотеки (шрифты, libgtk).envсsecrets.*— пароли и URL staging не хранят в коде. Секреты задают в Settings → Secrets and variables → Actions.upload-artifactприfailure()— HTML-отчёт и trace только когда тесты упали, чтобы экономить место.
Связь с процессом разработки
На GitHub можно включить защиту ветки main: merge разрешён только если workflow зелёный. Подробнее — забота о коде и данных. Разработчик открывает PR → Actions гоняет E2E → ревьюер видит галочку или красный крест.
Тот же принцип работает в Azure DevOps, GitLab CI, Jenkins — отличаются синтаксис YAML и названия сущностей; идея "тесты на каждый PR" одна.
Flaky tests — почему падают и что делать
Flaky — тест без явной причины в diff’е коммита. Частые источники:
| Причина | Симптом | Что помогает |
|---|---|---|
| Гонка за данные | "Пользователь уже существует" | Отдельный пользователь на тест; сброс БД или транзакция |
| Зависимость от времени | Падает ночью из-за часового пояса | Фиксированный timezone в CI, мок часов |
| Продакшен API | 429, rate limit | Mock сервера (Playwright route) или контрактные стабы |
| Медленный staging | Таймаут на goto | Увеличить timeout только для тяжёлых сценариев; чинить perf |
| Порядок тестов | Второй тест видит мусор от первого | Изоляция: test.describe.serial только где нужно; иначе — параллель и чистые данные |
Quarantine — пометить нестабильный тест (test.fixme или отдельный тег), завести задачу, чинить по приоритету. Отключать весь E2E-suite из-за одного flaky — путь к регрессиям в production.
Пример сценария "логин" (скелет)
import { test, expect } from '@playwright/test';
test('успешный вход', async ({ page }) => {
await page.goto('/login');
await page.getByLabel('Email').fill(process.env.E2E_USER);
await page.getByLabel('Пароль').fill(process.env.E2E_PASSWORD);
await page.getByRole('button', { name: 'Войти' }).click();
await expect(page).toHaveURL(/\/dashboard/);
await expect(page.getByText('Добро пожаловать')).toBeVisible();
});
Переменные окружения подставляет CI; локально — файл .env (добавьте .env в .gitignore).
Практика на Learn
| Модуль | Фокус |
|---|---|
| Playwright — автоматизация | Селекторы, codegen, отчёты, trace |
| GitHub Actions — введение | Workflow, secrets, кэш зависимостей |
Итоги
E2E проверяет продукт целиком через браузер. Playwright даёт стабильные ожидания, кроссбраузерность и отчёты для разбора падений. GitHub Actions встраивает прогон в привычный цикл: коммит → PR → зелёный или красный pipeline → merge с уверенностью.
Начните с одного сценария (логин или главная), подключите workflow, добавляйте сценарии по мере роста боли от регрессий — так пирамида остаётся управляемой.
См. также
- Тестирование в проекте — пирамида, unit и integration
- GitHub Actions — CI/CD рецепты — workflow для pytest, Node, matrix и деплоя
- Проверка надёжности под нагрузкой — нагрузка после функциональных E2E
- Git — ветки, PR и защита
main - Контейнеризация — образ приложения для staging в CI
- Микросервисы и интеграция — когда E2E бьёт в API-gateway и несколько сервисов
В подборках
Статья входит в тематические подборки и блок "С чего начать?" на главной. Соседние шаги того же маршрута:
Справочники — Справочник по Tempo, Справочник по Azure Repos Git, Справочник по Loki, Справочник по SOAP, Справочник по Kibana, Справочник по gRPC.