> ✍️ О себе: Пишу боевые юнит-тесты для React уже 4 года в продакшене, а не для «todo-list». Знаю школы тестирования, прошёл путь от Enzyme до RTL — и создал свою библиотеку
shallowly
. Пишу код вслепую в Neovim и уверен: времени «нет» только у тех, кто предпочитает кликать мышью в IDE вместо написания тестов.
Словарь терминов
Юнит (Unit) — наименьшая тестируемая часть приложения. В React это обычно компонент, хук или функция в изоляции
Shallow rendering — поверхностный рендеринг компонента без рендеринга его дочерних компонентов
Mock — имитация зависимости, которая заменяет реальную реализацию в тестах
TDD (Test-Driven Development) — разработка через тестирование, когда тест пишется до кода
CI/CD — автоматизированная система сборки и деплоя
Исповедь бывшего кликера
Когда-то мой чек-лист «готова ли фича» выглядел как молитва джуна: открыть браузер, нажать пару кнопок, убедиться, что в консоли нет красного цвета (желтое — это нормально, да?), и смело делать merge.
В те времена React был еще зеленым, Backbone уходил в архив, а модные парни на конференциях говорили про какое-то тестирование. Я слушал их как индеец — много слов, мало понимания.
Проект жил быстро, но баги плодились еще быстрее. Каждый хот-фикс тянул за собой новый хот-фикс, как матрешка багов. Я начал бояться рефакторинга сильнее, чем дантист боится собственных зубов.
Переломный момент настал ночью перед релизом. Один пропущенный null
-чек положил всю систему авторизации. Пока я в 3 утра откатывал релиз, понял: так больше жить нельзя.
Эволюция подходов: как я перестал бояться и полюбил тесты
Этап 1: Скриншот-тестирование «на коленке»
Первым делом попробовал snapshot-тесты. Логика была простая: «Если diff не красный — значит все хорошо». Быстро понял, что это как охранять дом сигнализацией, которая срабатывает от пролетающей мухи. Поменял padding на 2px — тест красный. Исправил опечатку в тексте — тест красный. Добавил запятую — красный.
Этап 2: E2E через страдания
Потом открыл для себя Cypress. Казалось — вот оно, решение! Прогоняю весь пользовательский сценарий, имитирую клики, получаю уверенность.
Но оказалось, что E2E тесты как дорогая машина: выглядят круто, но постоянно ломаются, требуют ухода, и когда что-то не работает — фиг поймешь где проблема. Плюс ждать 10 минут результата теста — это как ждать загрузки интернета в 2003 году.
Этап 3: Юнит-тесты как просветление
И тут я понял истину: юнит-тесты это не про количество, а про качество жизни разработчика. Они:
Ловят очевидные ошибки еще до браузера (null-check привет!)
Дают смелость резать мертвый код без страха что-то сломать
Документируют намерения лучше любых комментариев
Работают молниеносно — фидбек за пару миллисекунд, а не минуты
Сейчас в нашем репозитории 3000+ тестов, а CI за 2–3 минуты говорит, можно ли деплоить. Да, писать тесты — «дорого», но постоянно чинить баги в сложном коде да еще и продакшене дороже в разы.
Типы тестирования: зоопарк подходов
Рассмотрим основные типы тестирования, чтобы понимать, откуда ноги растут. Кстати, интересный факт: если бы у тестирования был свой Оскар, то награда за лучшую мужскую роль досталась бы Кенту Беку за вклад в разработку через тестирование (TDD) в конце 90-х. Но давайте по порядку.
🧪 Юнит-тесты
Родоначальник: Кент Бек (да, он снова здесь) в рамках методологии экстремального программирования (XP) в конце 90-х.
Современные амбассадоры:
Мартин Фаулер, который до сих пор яростно спорит о том, что считать «юнитом»
Роберт (Боб) Мартин (Дядя Боб), который считает, что если у вас нет 100% покрытия, вы, вероятно, что-то упускаете
Стив Фримен (один из авторов «Growing Object-Oriented Software»), который знает о юнит-тестах всё и даже больше
Дэвид Хейнемейр Ханссон (DHH), который сначала ругал TDD, а потом полюбил его
Философия: Создавались как основа TDD (разработки через тестирование). Идея в том, чтобы проверять мельчайшие кирпичики кода в изоляции, как инженер проверяет шестерёнки перед сборкой механизма. Юниты — это про уверенность, что каждая функция работает как часы, даже если вокруг всё горит.
Что тестируют: Отдельные функции, компоненты в изоляции
Скорость: Молниеносно (миллисекунды)
Плюсы: Быстро, детерминированно, легко отлаживать
Минусы: Не видят проблем интеграции
// Пример юнит-теста
test("Calculator сумма двух чисел", () => {
const calc = new Calculator();
expect(calc.add(2, 3)).toBe(5);
});
🔄 Интеграционные тесты
История: Появились как ответ на проблему «все тесты проходят, а продакшен падает». Впервые систематизированы Майклом Фезерсом в середине 2000-х.
Современные гуру:
Сэм Ньюмен, автор книги «Монилитный кризис» и ярый проповедник микросервисов
Мартин Фаулер, который считает, что если ваш интеграционный тест не падает хотя бы раз в неделю, вы, вероятно, тестируете не всё
Ибрагим Чешмелиоглу, автор «Testing JavaScript Applications», который знает о тестировании фронтенда всё
Сэнди Метц, которая умеет объяснять сложные концепции тестирования так, что даже менеджеры кивают
Философия: Родились из осознания, что юниты — это здорово, но код живёт не в вакууме. Интеграционные тесты проверяют, как шестерёнки крутятся вместе. Их цель — поймать проблемы на стыке модулей, когда один разработчик думал, что возвращает null
, а другой ждал строку.
// Пример интеграционного теста
test("Button должен вызывать onClick при клике", () => {
const onClick = jest.fn();
const { getByText } = render(<button>Click me</button>);
fireEvent.click(getByText("Click me"));
expect(onClick).toHaveBeenCalledTimes(1);
});
// да да для меня это интеграционный тест . Чуть позже раскажу почему.
Что тестируют: Взаимодействие между компонентами/модулями
Скорость: Средняя (секунды)
Плюсы: Видят проблемы интеграции, ближе к реальности
Минусы: Сложнее отлаживать, медленнее
🌐 E2E тесты
История: Зародились в эпоху мейнфреймов, но второе рождение получили с приходом веба. Селин Ласик (Selenium, 2004) — наш герой, который подарил нам все эти browser.click
()
.
Современные гуру:
Гилен Дебелп (Gleb Bahmutov), который, кажется, может написать E2E-тест быстрее, чем вы успеете произнести «флакки-тест»
Андрей Симонов, создатель CodeceptJS, который доказал, что E2E-тесты могут быть не только медленными, но и быстрыми
Филипп Харитонов, автор Playwright, который заставил всех поверить, что стабильные E2E-тесты — это не миф
Пол Гросс (Paul Grosse), который умудряется тестировать даже самые сложные веб-приложения, не теряя рассудка
Философия: Это взгляд сверху на всю систему глазами пользователя. Создавались, чтобы убедиться, что путь от логина до чекаута не превратится в квест «найди баг». E2E-тесты — это как генеральная репетиция перед премьерой: всё должно работать как в жизни, даже если под капотом творится хаос.
Что тестируют: Полные пользовательские сценарии
Скорость: Медленно (минуты)
Плюсы: Максимально близко к реальности
Минусы: Хрупкие, медленные, сложные в отладке
Модели тестирования: философские школы и их гуру
Как говорил один мой знакомый тестировщик: «Есть два типа команд: те, кто следует моделям тестирования, и те, кто делает вид, что их не существует». Давайте познакомимся с основными подходами и их создателями.
Далее у нас есть методология тестирования которые определяют сколько и каких тестов должно быть.
🛕 Пирамида тестирования (классика)
Авторство: Майк Кан (Mike Cohn) в книге «Succeeding with Agile» (2009).
Интересный факт: Изначально пирамида была перевернута, но потом Майк одумался (или просто перевернул слайд, история умалчивает).
Современные амбассадоры:
Джеральд Вайнберг, который до сих пор учит нас, что «чем ниже, тем лучше»
Лиза Криспин, соавтор «Agile Testing», которая знает о пирамиде всё и даже больше
Джанет Грегори, которая умеет объяснить пирамиду тестирования так, что даже бизнес-аналитики начинают её использовать
Кент Бек, который, кажется, успел оставить свой след во всём, что связано с тестированием
🌐 E2E (мало) — как вишенка на торте
🔄 Интеграционные (средне) — кремовая начинка
🧪 Юнит-тесты (много) — прочное бисквитное основание
Философия: Чем ниже уровень — тем больше тестов. Они дешевле и быстрее. Это как диета: если перевернуть пирамиду, получится торт, который упадёт вам на голову при первой же попытке деплоя.
Когда использовать: Стабильные большие проекты, команды с опытом, которые понимают разницу между тестом и кликбейт-заголовком.
💎 Тестовый алмаз
История: Зародился в недрах микросервисных команд в начале 2010-х, когда разработчики устали объяснять, почему их сервисы не работают вместе, несмотря на зелёные тесты.
Современные амбассадоры:
Ник Тюн (Nick Tune), который рисует такие схемы, что хочется распечатать их как постер и повесить на стену с подписью «Вот как надо!»
Сэм Ньюмен, который, кажется, знает о микросервисах и их тестировании всё
Крис Ричардсон, автор «Microservices Patterns», который умеет объяснять сложные концепции простыми словами
Мартин Фаулер, который успел высказаться практически обо всём в мире разработки, включая и эту модель
🌐 E2E (мало) — потому что никто не любит ждать
🔄 Интеграционные (МНОГО) — вот где настоящая магия
🧪 Юнит-тесты (средне) — на всякий случай
Философия: Если юнит-тесты — это проверка каждой шестерёнки по отдельности, то интеграционные — это когда вы собираете велосипед и надеетесь, что он поедет. Акцент на реальные взаимодействия компонентов, потому что в реальном мире никто не живёт в вакууме (кроме, возможно, некоторых менеджеров).
Когда использовать: Микросервисы, сложная интеграция и когда хочется почувствовать себя повелителем распределённых систем.
🍦 Мороженое (антипаттерн)
История: Никто не «придумал» это специально, но стало мемом благодаря командам, которые в 2000-х годах полагались только на Selenium и молитвы.
Известные последователи:
Тот самый тимлид в вашей компании, который говорит: «У нас нет времени на юнит-тесты, давайте просто накрутим E2E»
Джо Армстронг (создатель Erlang), который как-то сказал: «Единственный способ отлаживать код — это добавлять операторы вывода»
Все, кто когда-либо говорил: «Да ладно, у нас же CI/CD, пусть сами тесты падают»
Легендарный «Петя из отдела тестирования», который до сих пор верит, что ручное тестирование — это будущее
🌐 E2E (МНОГО) — потому что кому нужны тесты, если можно просто нажать F5?
🔄 Интеграционные (мало) — случайно затесались
🧪 Юнит-тесты (почти нет) — «это же лишний код»
Философия: Строим дом с крыши. Выглядит сладко, пока не начнётся дождь багов. А когда начинается — все дружно удивляемся, почему же так медленно и ничего не работает.
Результат: Медленно, дорого, нестабильно. Но зато можно сказать, что у вас «полная тестовая документация» (из 2000 тестов, которые падают в случайном порядке).
Кто так делает: Те, кто считает, что «тестирование — это когда QA посидел, понажимал кнопочки и сказал, что всё ок».
🏆 Тестовый трофей (Kent C. Dodds, ~2017)
🌐 E2E
🔄 Интеграционные (основа)
🧪 Юнит-тесты
⚡ Статический анализ
Контекст: Появился как ответ на сложности тестирования React-приложений. Кент, кстати, до сих пор уверяет, что это не он придумал, а всего лишь «систематизировал».
Ключевые адепты:
Кент С. Доддс (Kent C. Dodds), который, кажется, может написать тест быстрее, чем вы успеете сказать «почему это не работает?»
Мишель Титус (Michelle Titus), которая доказывает, что тестирование React может быть не только эффективным, но и приятным
Райан Флоренс (Ryan Florence), создатель React Router, который знает о тестировании React-приложений всё (и даже немного больше)
Марко Полетто (Marco Piraccini), который умеет объяснить, почему статический анализ — это не просто мода, а must-have
Философия: «Тестируй как пользователь» + акцент на интеграционные
Инструменты: React Testing Library
🧊 Тестовый куб (для больших систем)
Автор: Брайан Оккенфельс (Brian Okken), автор книги «Python Testing with pytest».
Фишка: Когда двумерных моделей стало мало, пришлось добавить третье измерение. Типичная история для программистов.
Многомерная модель с тремя осями:
Что: компоненты, контракты, интеграции
Как: юнит, e2e, нагрузочные тесты
Где: локально, CI, staging, production
Когда использовать: Энтерпрайз, микросервисы, большие команды
Школы юнит-тестирования
теперь почему я говорю что React Testing Library это не юнит тесты.
📚 Классическая школа (Detroit School)
Гуру:
Кент Бек, который, кажется, успел основать всё, что можно
Мартин Фаулер, который умеет объяснить, почему это работает
Сэнди Метц, которая знает о тестировании объектов всё
Дэвид Хейнемейр Ханссон (DHH), который сначала ругал TDD, а потом полюбил его
Принципы:
Тестируй поведение, а не реализацию
Минимум моков, максимум реальных объектов
Тест должен быть понятен без изучения кода
🏭 Лондонская школа (Mockist School)
Гуру:
Стив Фримен, соавтор «Growing Object-Oriented Software»
Нейт Шугармен, который умеет объяснить, зачем вообще нужны моки
Мартин Фаулер, который успел высказаться и на эту тему
Принципы:
Тотальная изоляция через моки
Тестируй взаимодействия между объектами
Каждый тест проверяет одну единицу
Тут идет различие что считать юнитом и в каком окружении он должен быть. Мое мнение что юнит это компонент в изоляции от окружающего мира как можно зависимостей особено от библиотек которые мы используем в этом юните.
Так что же насчёт Мартина Фаулера?
Когда речь заходит о тестировании, все почему-то ждут, что Мартин Фаулер даст однозначный ответ. Но он, как настоящий мудрец, предпочитает не вставать ни на чью сторону, оставляя место для манёвра:
Прагматичный баланс — он не фанатик ни одной из школ, а скорее адепт здравого смысла. «Тестируйте так, чтобы было эффективно, а не чтобы угодить гуру» — вот его негласный девиз.
Рефакторинг — наше всё — его подход к тестированию неотделим от рефакторинга. Если ваши тесты не дают вам уверенности при рефакторинге, значит, вы делаете что-то не так. Или делаете это с закрытыми глазами.
Догмам — нет! — Фаулера раздражают священные войны между адептами разных подходов. «Гибкость, а не фанатизм» — мог бы быть его девиз, если бы он любил девизы.
Unit-тесты как страховка — в своей настольной книге «Refactoring» он делает ставку на unit-тесты, но без фанатизма. Примерно как с кофе: одна чашка бодрит, десять — вызывают тремор.
Реализм превыше всего — он признаёт, что в реальных проектах идеальная пирамида тестирования часто превращается в нечто среднее между алмазом и снежинкой, и это нормально. Главное — понимать, зачем вы это делаете.
Если бы Фаулеру пришлось выбирать модель тестирования, он, вероятно, выбрал бы «пирамиду с человеческим лицом» — с акцентом на unit-тесты для рефакторинга и достаточным количеством интеграционных тестов, чтобы не краснеть перед продакшеном. Но он бы точно предупредил: «Любая модель — это всего лишь модель. Не путайте карту с местностью, а тесты — с реальным кодом».
Практические выводы
После 4 лет боевого тестирования вот что я понял:
Начинайте с юнитов — они дают максимум пользы за минимум времени
Пирамида работает для большинства проектов, но не бойтесь адаптировать
Качество важнее количества — лучше 10 хороших тестов, чем 100 плохих
Тесты — это инвестиция — но тесты не должны быть дорогими, потом окупается с лихвой
Рекомендуемая литература
Книги на русском
Мартин Фаулер, «Рефакторинг. Улучшение существующего кода» — классика о тестировании как основе для безопасного рефакторинга.
Кент Бек, «Разработка через тестирование» — манифест TDD от его создателя.
Стив Фримен, Рой Ошероув, «Тестирование JavaScript-приложений» — про тестирование в экосистеме JavaScript.
Виктор Сапаров, «Тестирование Дот Ком» — отличное введение в тестирование от российского автора.
Сергей Аверин, «Тестирование программного обеспечения» — базовый учебник по тестированию.
Владимир Хориков. «Принципы юнит-тестирования» — Фундаментальная книга отечественного автора про юнит тестироание.
Лукас да Коста. «Тестирование JavaScript» — Довольно не плохая книга, которая в полной мере раскрывает тему автоматизированного тестирования.
Книги на английском
Robert C. Martin, "Clean Code" — главы про тестирование обязательны к прочтению.
Kent Beck, "Test-Driven Development: By Example" — must-read для понимания TDD.
Luca Mezzalira, "Frontend Architecture for Design Systems" — про тестирование в современных фронтенд-архитектурах.
Eric Elliott, "Composing Software" — о композиционном подходе к тестированию.
Статьи и онлайн-ресурсы
На русском:
Пирамида тестирования на практике — разбор на Хабр
Юнит-тесты: введение — отличный учебник на learn.javascript.ru
Тестирование React-приложений — официальная документация React
На английском:
The Testing Trophy and Testing Classifications — современный взгляд на классификацию тестов
TestPyramid — классическая статья Мартина Фаулера
React Testing Examples — примеры тестирования React-компонентов
Testing Library — принципы тестирования от создателей React Testing Library
Эти ресурсы помогут не только разобраться в «как» и «почему» тестирования, но и покажут разные подходы и философии, стоящие за разными школами тестирования.
В следующих частях разберем, почему юнит-тесты — это турбо-режим для разработки, как TDD меняет подход к коду, и почему я создал библиотеку shallowly
вместо того, чтобы мучиться с RTL.
Следующая статья: «Почему именно юнит-тесты: от TDD до архитектуры компонентов»
P.S. Если вы до сих пор тестируете только через браузер — не расстраивайтесь. Мы все когда-то были такими. Главное — начать. А с этими книгами вы точно поймете, что к чему в мире тестирования.