Для фичи чекаута нужны три E2E-теста, а не тридцать: один для happy path, один для ошибки платежа, один для товара не в наличии. Остальные edge case дешевле и стабильнее в виде юнит и API-тестов. Статья объясняет три слоя пирамиды, что происходит когда команды переворачивают её в мороженое, и современный вариант (тестовый трофей) который подходит для фронтенд-тяжёлых приложений.

Исходная модель

Майк Кон представил пирамиду тестирования в 2009 году. Исходная версия имела три слоя:

        /\
       /  \
      / E2E \       <- мало, медленно, дорого
     /--------\
    / Integration\  <- умеренно
   /--------------\
  /   Unit Tests   \ <- много, быстро, дёшево
 /------------------\

Юнит-тесты (снизу): тестируют отдельные функции или классы в изоляции. Миллисекунды на выполнение. Сотни или тысячи. Немедленная обратная связь. Интеграционные тесты (в середине): тестируют как компоненты работают вместе: сервисный слой обращающийся к базе данных, API-эндпоинт с реальным middleware. Секунды на выполнение. Десятки-сотни. E2E-тесты (наверху): тестируют полную систему через браузер или API-клиент. Минуты на полный сьют. Десятки-несколько сотен.

Форма имеет значение: много снизу, меньше наверху. Пирамида.

Почему форма важна

Если перевернуть пирамиду: много E2E-тестов, мало юнит-тестов, получаешь:

  • Медленный сьют (E2E-тесты работают в 10–60 раз дольше юнит-тестов)
  • Флакующие тесты (больше движущихся частей = больше режимов сбоев)
  • Плохую отладочную информацию (падение E2E говорит что что-то не так; падение юнит-теста говорит что именно)
  • Дорогую поддержку (изменения UI ломают E2E-тесты; API меняются реже)

Классический антипаттерн: мороженое. Крошечный слой юнит-тестов, никакого интеграционного слоя, и масштабный E2E-сьют который работает 45 минут и падает случайно.

Современная интерпретация

Исходная пирамида появилась до микросервисов, serverless и фреймворков вроде Playwright которые делают E2E-тесты значительно надёжнее. Современная версия это признаёт:

        /\
       /  \
      / E2E \         <- smoke-тесты, критические пути
     /--------\
    /  API/Svc \      <- интеграция, contract-тесты
   /--------------\
  /  Component/Unit \ <- бизнес-логика, утилиты
 /--------------------\

Конкретные пропорции варьируются в зависимости от команды и типа продукта. CRUD-API может иметь очень мало E2E-тестов и большое API-покрытие. React-дашборд со сложным UI может иметь больше компонентных тестов и меньше юнит-тестов.

Принцип остаётся тем же: опирайся на более дешёвые, быстрые и стабильные тесты. Резервируй E2E для сценариев которые можно верифицировать только через полный стек.

Применение пирамиды к Playwright-проекту

Что относится в E2E-тесты

  • Критические пользовательские пути (вход → чекаут → подтверждение)
  • Кросс-сервисная интеграция (фронтенд + бэкенд + база данных вместе)
  • Специфичное для браузера поведение (загрузка файлов, несколько вкладок, OAuth-редирект)
  • Smoke-тесты для продакшн-деплоев

Что не относится в E2E-тесты

  • Сообщения об ошибках валидации (тестируй в юнит-тестах логики валидации)
  • Каждый edge case сложного алгоритма (юнит-тест)
  • API-ответы в изоляции (API-тест)
  • Все 15 перестановок формы (разбивка на классы, выбери 3–4 представительных случая для E2E)

Конкретный пример

Тестируется фича размещения заказа. Требования:

1. Пользователь добавляет товар в корзину

2. Пользователь переходит к чекауту

3. Пользователь заполняет данные доставки

4. Пользователь оплачивает

5. Показывается подтверждение заказа

6. Пользователю отправляется email

Юнит-тесты покрывают

  • Логику расчёта цены (скидки, налог, стоимость доставки)
  • Рендеринг шаблона email
  • Функцию валидации адреса
  • Округление суммы платежа

API/интеграционные тесты покрывают

  • POST /orders создаёт запись в базе данных
  • POST /orders с невалидным платежом возвращает 422
  • Переходы статуса заказа (pending → confirmed → shipped)
  • Сервис email вызывается с правильными параметрами

E2E-тесты покрывают

Happy path (один тест): полный заказ от корзины до подтверждения. Ошибка платежа (один тест): карта отклонена, ошибка показана, заказ не создан. Нет в наличии (один тест): товар недоступен, пользователь перенаправлен с сообщением.

Всё. Три E2E-теста для целой фичи чекаута. Юнит и API-тесты покрывают edge case: E2E покрывает флоу которые важнее всего пользователям.

Тестовый трофей (альтернативная модель)

Кент С. Доддс предложил тестовый трофей в 2018 году, который адаптирует пирамиду для JavaScript-фронтендов:

          /\
         /  \          <- E2E (мало)
        /----\
       / Integ \       <- интеграция (большинство)
      /----------\
     /   Unit     \    <- юнит (некоторые)
    /--------------\
   /    Static      \  <- типы, линтинг (всегда)
  /------------------\

Ключевое отличие: интеграционные тесты наверху середины. Для React/Vue/Next.js-приложений "интеграционные тесты" означает рендеринг компонентов с их реальными зависимостями (реальные API-вызовы к тестовому серверу или версия с моком на уровне сети). Философия React Testing Library: тестируй как пользователи взаимодействуют с компонентом, а не детали реализации.

Обе модели валидны. Пирамида хорошо подходит для бэкенд-систем. Трофей подходит для фронтенд-тяжёлых приложений. Любая из них лучше мороженого.

Правило большого пальца

Когда собираешься писать E2E-тест: спроси себя, что реально верифицирует этот тест, что нельзя было бы верифицировать на более низком уровне?

Если ответ "что фронтенд и бэкенд соединены и данные корректно проходят через весь стек": пиши E2E-тест.

Если ответ "что валидация формы показывает ошибку для пустого поля email": это юнит-тест для функции валидации и компонентный тест для формы. E2E-тест здесь был бы просто шумом.

→ See also: Дымовое тестирование vs регрессионное: в чём разница? | Компонентное тестирование в Playwright: тестирование React/Vue компонентов в изоляции | Практики автоматизации тестирования, которые реально важны