Unit-тесты: изоляция и хорошие практики
unit-тест проверяет одну единицу логики изолированно; хороший unit-тест быстр, детерминирован и диагностирует конкретную проблему
Почему это важно: плохо написанные unit-тесты ломаются при безопасном рефакторинге, дают ложные сигналы и отбивают желание писать тесты
Главная идея
AAA-паттерн (Arrange-Act-Assert) структурирует тест; тест должен проверять одно поведение; название — документация
Как это выглядит на практике
- Arrange: создаём объект User с role: :admin.
- Act: вызываем user.can?(:delete_posts).
- Assert: ожидаем true.
- Тест называется: 'admin can delete any post'.
- Тест падает с понятным сообщением: expected true, got false.
Что происходит под капотом
- Test double: заменитель зависимости в тестах. Виды: stub (возвращает значение), mock (проверяет вызовы), spy (записывает вызовы), fake (упрощённая реализация).
- Dependency injection: зависимости передаются извне, что позволяет заменять их в тестах.
- Parametrized tests: один тест с разными входными данными. Избегает дублирование.
- describe/context/it — структура для группировки тестов по поведению.
Типичные ошибки и заблуждения
- Ошибка: один тест на один метод. Один тест на одно ожидаемое поведение. Метод может иметь несколько тестов.
- Ошибка: чем больше assertions в тесте, тем лучше. Одна assertion на тест; при падении сразу ясна причина.
- Ошибка: тест, проверяющий реализацию, лучше документирует. Тестируй публичный интерфейс; рефакторинг внутренностей не должен ломать тесты.
- Ошибка: shared state между тестами ускоряет setup. Shared state — источник flaky тестов и скрытых зависимостей между тестами.
Ключевые выводы
- AAA: Arrange → Act → Assert. Каждый раздел явно разделён.
- Одна assertion на тест: при падении сразу ясна причина.
- Название теста — документация: 'should return 403 when user is not owner'.
- Изоляция: тест не зависит от других тестов, БД, времени, сети.
Термины урока
Связь с работой backend-разработчика
Правило хорошего теста: FIRST — Fast, Isolated, Repeatable, Self-validating, Timely. Если тест нарушает любой из этих принципов — это сигнал к рефакторингу.
Мини-разбор реальной ситуации
Разработчик пишет тест, который создаёт пользователя в реальной БД, вызывает сервис и проверяет результат. Тест: медленный (100ms), непредсказуемый (зависит от состояния БД), ломается при очистке. После инъекции мок-репозитория: тест 2ms, всегда зелёный, тестирует только бизнес-логику.
Что запомнить
- AAA структура делает тест читаемым.
- Тест = документация: назовите тест как бизнес-требование.
- Изоляция: каждый тест независим от других.
Итог
Хорошие unit-тесты — это не бюрократия, а дизайн-инструмент. Они заставляют думать о зависимостях и интерфейсах до написания кода.