Перейти к основному содержимому

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();
});

Что происходит по шагам:

  1. test(...) — объявление одного сценария; имя попадёт в отчёт CI.
  2. page.goto — открыть URL и дождаться загрузки (по умолчанию — событие load).
  3. expect(page).toHaveTitle — заголовок вкладки совпадает с регулярным выражением.
  4. 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, мок часов
Продакшен API429, rate limitMock сервера (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, добавляйте сценарии по мере роста боли от регрессий — так пирамида остаётся управляемой.


См. также


В подборках

Статья входит в тематические подборки и блок "С чего начать?" на главной. Соседние шаги того же маршрута:

СправочникиСправочник по Tempo, Справочник по Azure Repos Git, Справочник по Loki, Справочник по SOAP, Справочник по Kibana, Справочник по gRPC.