Пройдя всю книгу, стоит собрать её в одну проверяемую картину: как отличить профессиональный тест от любительского, глядя на код. Это не чек-лист ради галочек, а способ мышления - набор вопросов, которые честный автор задаёт себе прежде, чем считать тест готовым. Все критерии ниже мы уже разбирали порознь; здесь они сходятся в один взгляд, потому что именно вместе они и определяют, стоит ли тесту доверять.
Первое - имя. Хорошее имя описывает условие и ожидаемый исход на языке поведения: 'возвращает ошибку при пустом email', а не 'тест валидации'. По имени должно быть понятно, что сломалось, ещё до чтения тела. Второе - тест проверяет наблюдаемое поведение, а не приватную реализацию: результат и видимый эффект, а не то, какой внутренний метод вызвался. Первый переживает рефакторинг, второй ломается от переименования.
Третье - покрыты значимые классы входа: нормальный, граничный, некорректный и сбойный. Именно граница и ветка ошибки чаще всего прячут баги, и тест на один счастливый путь их не видит. Четвёртое - независимость: результат не зависит от сети, часов, порядка запуска и общего состояния. Тест, зелёный в одиночку и красный в наборе, нарушает этот критерий и не заслуживает доверия, пока причина не устранена.
Пятое - подмены стоят на настоящих границах. Мок оправдан на сети, файловой системе, платёжном шлюзе, системных часах; мокать доменную логику, которую можно передать зависимостью, - значит проверять собственные заглушки. Шестое - интеграционный риск покрыт интеграционным тестом, а не сымитирован юнит-моками: поведение базы проверяют реальной тестовой базой, а не моком ORM, который согласится с чем угодно.
Седьмое - падение читается. Когда тест краснеет, из Expected/Received и имени должно быть ясно, что именно не так, без раскопок в отладчике по каждому случаю. Понятный diff - часть контракта теста, а не приятный бонус. Восьмое, обобщающее, - безопасный рефакторинг не должен ломать тест: если изменение, сохраняющее поведение, валит проверку, тест держится за форму реализации, а не за обещание, и будет мешать, а не защищать.
| Критерий | Профессиональный тест | Любительский |
|---|---|---|
| Имя | условие + исход, язык поведения |
| 'тест функции', 'работает' |
| Что проверяет | наблюдаемое поведение | приватный вызов, внутренний метод |
|---|
| Входы | normal / boundary / invalid / failure | только счастливый путь |
|---|
| Независимость | от сети, часов, порядка, state | зелёный один, красный в наборе |
|---|
| Подмены | на реальной границе | мок доменной логики |
|---|
| Интеграция | реальная тестовая база | мок ORM согласится с чем угодно |
|---|
| Падение | понятный Expected/Received | неясно, что сломалось |
|---|
| Рефакторинг | не ломает при том же поведении | падает от переименования |
|---|
Эти восемь пунктов складываются в один принцип, ради которого написана вся книга. Тест защищает наблюдаемое обещание кода, а не форму его реализации. Обещание - это то, что видит и на что рассчитывает пользователь функции, компонента, API: заданный вход даёт заданный выход, ошибка сообщается, контракт соблюдён. Форма - это как именно всё устроено внутри, и она вправе меняться, пока обещание держится.
Отсюда практический смысл всей дисциплины. Хорошие тесты дают свободу менять реализацию без страха: пока обещание цело, набор молчит, а стоит его нарушить - краснеет точно и по делу. Плохие тесты делают обратное - привязываются к реализации и наказывают за любой рефакторинг, пока команда не начинает бояться собственного кода. Разница не в инструменте и не в числе тестов, а в том, что именно они утверждают.
Проверьте свой тест этими вопросами - и вы увидите не строку покрытия, а обещание, которое она стережёт. Пишите набор, который говорит правду о поведении: молчит, пока обещание держится, и краснеет ровно тогда, когда оно нарушено. Такой набор - не бюрократия и не страховка ради метрики, а то, что позволяет уверенно менять систему годами. Это и есть профессиональное тестирование, а не установка инструмента и галочка в отчёте.