Инженерия ПО

Unit Testing

Код написан, вручную проверен, задеплоен. Через неделю - баг в продакшне в той самой функции. «Но я же проверял!» Разница между «проверял руками» и «есть тесты» - это разница между надеждой и уверенностью. Юнит-тесты не убирают баги, они делают их невозможными для повторного появления.

  • **Google** требуют 80%+ branch coverage для кода в production; каждый PR должен добавлять тесты на новые пути
  • **Netflix** практикуют chaos engineering - намеренные сбои в продакшне. Это возможно только при наличии надёжной тестовой базы
  • **NASA** использовали формальную верификацию и тестирование для Mars Rover: ни один баг в flight software не может быть исправлен после запуска

Arrange-Act-Assert паттерн

Тест без структуры - загадка. Непонятно, что готовится, что проверяется, и что является ожидаемым результатом. **Arrange-Act-Assert** (AAA) - это три явные фазы, которые превращают тест в читаемую спецификацию поведения.

**Arrange** - подготовить всё необходимое: объекты, данные, моки. **Act** - выполнить ровно одно действие: вызов функции или метода. **Assert** - проверить результат или побочный эффект. Одна функция = один тест = одна проверяемая гипотеза.

**Правило одной assert-фазы:** если в тесте несколько несвязанных проверок - разбейте на несколько тестов. Когда тест упадёт, сразу будет ясно что именно сломалось.

В фазе Act правильно выполнить:

Mocks: когда и как

Юнит-тест проверяет одну единицу изолированно. Но реальный код зависит от базы данных, HTTP-сервисов, файловой системы. **Mock** - замена зависимости, которая контролирует входные данные и проверяет, как тестируемый код взаимодействует с зависимостью: какие методы вызывались, с какими аргументами, сколько раз.

Mock отличается от других тест-дублёров тем, что содержит **ожидания**: тест проверяет не только результат, но и то, что код правильно «разговаривал» с зависимостью. Это полезно, когда важна сама коммуникация, а не только результат.

**Когда mock лишний:** если тест мокирует 5 зависимостей - скорее всего, класс делает слишком много (нарушение SRP). Mock-тяжёлые тесты - сигнал переработать архитектуру.

Mock отличается от Stub тем, что:

Stubs vs Fakes vs Spies

В тестировании существует семейство **тест-дублёров** (test doubles) - замен реальных зависимостей. Их часто путают, называя всё «моками». Но каждый тип решает разную задачу, и правильный выбор делает тест проще.

ТипРеальная логика?Проверяет вызовы?Когда использовать
StubНетНетКонтроль входных данных
FakeДа (упрощённая)НетНужна логика без внешних зависимостей
SpyДа (реальная)ДаПроверить побочный эффект
MockНетДаПроверить коммуникацию с зависимостью

Нужно протестировать сервис с репозиторием. Важна только логика сервиса, данные из репозитория фиксированные. Какой тип дублёра подходит?

Code Coverage: метрики и ловушки

100% code coverage - не значит отсутствие багов. Coverage измеряет, какие строки кода выполнялись во время тестов, но не проверяет правильность ожидаемого поведения. Можно написать тесты без единого `expect()` - coverage будет 100%, тесты бессмысленными.

Четыре вида coverage: **statement** (строки), **branch** (ветки if/else), **function** (функции), **line** (строки с учётом смежных). Самый полезный на практике - **branch coverage**: он заставляет тестировать оба пути в каждом условии.

**Практическое правило:** 80% branch coverage в критических модулях (бизнес-логика, финансы) - разумная цель. 100% coverage везде - чаще вредит, чем помогает: команда пишет тесты ради метрики, а не ради уверенности.

100% code coverage означает надёжный и корректный код.

Coverage показывает, какой код выполнялся, но не проверяет корректность поведения. Тест без expect() даёт 100% coverage.

Метрика coverage стимулирует писать тесты ради строк, а не ради уверенности. Ценен branch coverage + значимые assertions, а не число.

Проект имеет 95% statement coverage. Это гарантирует:

Unit Testing

  • AAA (Arrange-Act-Assert): один тест проверяет одну вещь, три явные фазы делают тест читаемой спецификацией
  • Mock проверяет взаимодействие (сколько раз вызван, с какими аргументами); используй когда важна коммуникация
  • Stub, Fake, Spy - разные дублёры для разных задач; выбирай простейший подходящий
  • Coverage - метрика выполнения кода, не качества; branch coverage полезнее statement; 80% в критических модулях разумная цель

Связанные темы

Юнит-тесты - основа пирамиды тестирования:

  • Code Review — Тесты - первое что проверяется в ревью
  • CI/CD пайплайны — Тесты запускаются в CI при каждом пуше
  • Рефакторинг — Тесты дают уверенность при рефакторинге

Вопросы для размышления

  • Где в текущем проекте тесты дают реальную уверенность, а где - только покрывают строки ради метрики?
  • Как понять, что код стало труднее тестировать из-за архитектуры, а не из-за сложности задачи?
  • Почему тесты для пограничных случаев (null, пустой список, максимальное значение) важнее тестов happy path?

Связанные уроки

  • alg-01-big-o
Unit Testing

0

1

Войти