Unit, интеграционные и E2E-тесты: в чём разница
Основы3 минРедакция Innovialabs
Коротко
Unit-тест проверяет одну функцию в изоляции, интеграционный — совместную работу частей системы, E2E — сценарий пользователя целиком; пропорцию выбирают по тому, где в проекте чаще всего ломается.
Коротко о разнице
Три типа тестов отличаются масштабом — тем, какую часть системы они проверяют за раз:
| Тип | Что проверяет | Скорость | Стабильность | Что найдёт |
|---|---|---|---|---|
| Unit | одну функцию или класс в изоляции | очень быстро | высокая | ошибки логики и граничные случаи |
| Интеграционный | связку модулей: код + база, сервис + API | средне | средняя | ошибки на стыках, неверные запросы, контракты |
| E2E | сценарий пользователя через интерфейс | медленно | ниже | сломанные пользовательские пути целиком |
Чем выше уровень, тем больше уверенности даёт один тест и тем дороже он в написании, запуске и поддержке.
Unit-тесты
Unit-тест берёт маленький кусок кода и проверяет его без внешних зависимостей. База данных, сеть и время заменяются моками или стабами.
// calcDiscount.test.js
import { calcDiscount } from "./calcDiscount";
test("скидка не превышает цену", () => {
expect(calcDiscount(100, 150)).toBe(100);
});
Хороши для: расчётов, валидации, форматирования, бизнес-правил с множеством условий.
Слабое место: все unit-тесты могут быть зелёными, а приложение при этом не работает — потому что модули неправильно соединены между собой.
Интеграционные тесты
Интеграционный тест проверяет, что несколько частей работают вместе. Например: HTTP-запрос к вашему API доходит до обработчика, данные сохраняются в настоящую тестовую базу, ответ имеет нужную структуру.
Типичные примеры:
- эндпоинт API + тестовая база данных;
- компонент интерфейса вместе с дочерними компонентами и состоянием;
- сервис + очередь сообщений;
- ваш код + внешний API в sandbox-режиме или его подменой на уровне сети.
Хороши для: проверки контрактов, SQL-запросов, сериализации, прав доступа.
E2E-тесты
End-to-end тест запускает приложение как пользователь: открывает браузер или мобильный эмулятор, нажимает кнопки, заполняет формы и проверяет результат. Популярные инструменты — Playwright и Cypress для веба.
Хороши для: критичных путей — регистрация, вход, оформление заказа, оплата в тестовом режиме.
Слабое место: медленные и чувствительные к окружению. Если таймауты, анимации и тестовые данные не продуманы, появляются flaky-тесты, которые падают через раз, и команда перестаёт им доверять.
Пирамида и трофей
Пирамида тестирования (идея Майка Кона): в основании много unit-тестов, в середине меньше интеграционных, на вершине немного E2E. Логика — быстрые и дешёвые тесты должны составлять основу.
Трофей тестирования (модель Кента Доддса): внизу статический анализ (типы, линтер), затем немного unit, самая широкая часть — интеграционные тесты, сверху немного E2E. Аргумент — интеграционные тесты ближе к реальному использованию и дают больше уверенности на единицу усилий, а современные инструменты сделали их достаточно быстрыми.
Обе модели не про точные проценты, а про направление: много дешёвых проверок, мало дорогих.
Как выбрать пропорцию для своего проекта
Ориентируйтесь на то, где у вас реально живут ошибки:
- Сложная бизнес-логика (расчёты, тарифы, правила) — больше unit-тестов.
- Много интеграций (API, база, платёжки, CRM) — упор на интеграционные тесты.
- Интерфейс с типовыми CRUD-экранами — интеграционные тесты компонентов плюс несколько E2E на ключевые сценарии.
- Критичные деньги или данные — обязательно E2E на эти пути.
Практичный старт: возьмите 3–5 самых важных пользовательских сценариев и покройте их E2E. Затем добавляйте интеграционные тесты на каждый баг, найденный в продакшене, и unit-тесты на сложную логику.
Частые ошибки
- Тестировать детали реализации вместо поведения — тесты ломаются при любом рефакторинге.
- Мокать всё подряд — тест проверяет моки, а не систему.
- Пытаться покрыть E2E все ветки логики — набор становится медленным и хрупким.
- Гнаться за процентом покрытия вместо проверки важных сценариев.
- Не запускать тесты в CI на каждый pull request.