Почему я не пытаюсь проверить всё через E2E

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

Можно написать E2E-сценарий, который откроет страницу, заполнит форму, отправит запрос и проверит результат. Но действительно ли ради каждой проверки нужно запускать браузер и поднимать приложение?

При работе с React-приложениями я придерживаюсь подхода, при котором разные типы проверок распределяются между несколькими уровнями.

1. Unit-тесты – Vitest Проверяю бизнес-логику, преобразования данных, валидацию и функции, которые можно протестировать независимо от интерфейса. Такие тесты быстро выполняются и позволяют локализовать проблему без лишних зависимостей.

2. Компонентные тесты – React Testing Library Здесь интересует уже поведение интерфейса: что происходит после нажатия кнопки, как работает валидация, меняется состояние компонента и вызываются ли нужные обработчики. Стараюсь проверять то, что доступно пользователю, а не внутреннюю реализацию компонента.

3. Интеграционные тесты – RTL + Storybook + MirageJS Один из наиболее интересных для меня уровней. Storybook помогает подготовить определённое состояние интерфейса, MirageJS – смоделировать ответы API, а RTL – проверить взаимодействие нескольких компонентов. Например, можно проверить сценарий с фильтрацией данных, переходами или обработкой ошибки без полноценного E2E.

4. Визуальные тесты – Storybook + Playwright Здесь задача другая: убедиться, что после изменений интерфейс выглядит так, как ожидается. Скриншотные проверки помогают обнаруживать визуальные регрессии, но не заменяют тестирование поведения интерфейса. При этом важна стабильность окружения: шрифты, анимации, время и асинхронная загрузка могут создавать ложные срабатывания.

5. E2E – Playwright Оставляю для ключевых пользовательских сценариев, где важно проверить взаимодействие приложения с реальным backend и прохождение процесса целиком. У таких тестов выше стоимость запуска и поддержки, поэтому покрывать ими абсолютно всё не вижу смысла.

Но есть нюанс.

Чем больше уровней тестирования, тем важнее не дублировать одни и те же проверки.

Если корректность вычислений уже проверена unit-тестом, необязательно повторять десятки комбинаций через браузер. А скриншот компонента не доказывает, что после действия пользователя отправится корректный API-запрос.

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

И ещё один момент: высокий процент code coverage сам по себе не гарантирует, что приложение работает правильно. Можно покрыть множество строк и всё равно пропустить важный пользовательский сценарий.

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

А где у вас проходит граница между интеграционными и E2E-тестами? Какие сценарии принципиально оставляете для проверки в браузере?

Почему я не пытаюсь проверить всё через E2E | Сетка — социальная сеть от hh.ru