Интеграционные или модульные тесты?
Май 2018
Когда читаешь книги Роберта Мартина про чистый код или про хороших программистов, сразу хочется покрыть весь свой код модульными тестами, встать на путь TDD и никогда с него не сходить. Выступления про вред интеграционных тестов еще больше укрепляют уверенность в необходимости модульных тестов. Утверждения в книгах и выступлениях кажутся верными, но применение новых практик в ежедневной работе вызывает боль.
Основные боли с которыми мне приходилось сталкиваться при работе с модульными тестами:
- рефакторинг, который сложнее чем переименование переменной, точно ломает какие-то тесты
- желание разработчиков сделать тесты модульными рождает много моков, которые приводят как к поломке тестов при рефакторинге, так и к ложно-положительным результатам тестов
- они не помогают разобраться в работе той или иной функциональности
Можно терпеть боль и продолжать применять практики. Можно поддерживать запал, смотря на пирамиду тестирования и повторяя "модульные тесты – основа пирамиды, их должно быть больше всего", – это снизит боль, но не устранит её причину.
Разобраться в причине боли мне помогли книги и статьи Кента Бека – человека, который придумал Extreme Programming и Test Driven Development. Если Роберт Мартин говорит с читателем языком догм: "Настоящий программист должен писать по TDD и точка!", то Кент Бек ведет диалог с читателем и описывает те ценности и принципы, которые привели его к конкретным практикам, в данном случае к TDD.
Основные принципы из которых появился TDD: продукт должен быть качественным, шаги для решения проблемы должны быть маленькими, процесс разработки должен идти потоком (flow). Именно из-за принципа маленьких шагов TDD ассоциируется с модульными тестами. Причем Кент не говорит таких догм как "тесты не должны ходить в базу данных" или "при тестировании одного объекта все его зависимости должны быть подменены".
Тесты должны давать вам уверенность в работоспособности продукта и возможность придумывать решение ваших проблем шаг за шагом, не сбивая ваш фокус. Все, больше ничего. Конкретные же практики зависят от конкретной ситуации.
Если при тестировании можно использовать in-memory базу данных и прогонять сотни тестов за доли секунды – используйте её! Ведь это увеличит уверенность в работоспособности продукта. Если вы не знаете конкретный путь к реализации функциональности, то сделайте первый шаг – напишите самый высокоуровневый тест в тех терминах, в которых вы сейчас понимаете задачу. И если этого теста будет достаточно для реализации функциональности без потери уверенности в ней, то смело его оставляйте.
Но если вам сложно понимать настройку ваших тестов, и это бьёт по уверенности в работоспособности продукта, то стоит их упростить. Например, перенеся большую часть проверок на уровнь ниже – в более модульные тесты, или создав абстракцию для настройки тестов. Если ваши тесты идут слишком долго и прерывают поток разработки, то можно их ускорить. Причем ускорять можно несколькими способами, например перестать ходить в базу данных или запускать тесты параллельно на нескольких базах. Любой способ хорош, если он не идет вразрез с принципами.
Не нужно бояться интеграционных тестов, если вам комфортно работать с ними. Если вам комфортно работать с вашими модульными тестами – продолжайте с ними работать. Главное – перестать слепо следовать догмам, прислушиваться к своим ощущениям и рефлексировать, стараясь взглянуть на проблему с нескольких точек зрения.