Перейти к содержимому
Модульное тестирование

Модульное тестирование

Модульное тестирование — проверка одного метода/функции в изоляции от внешних зависимостей (БД, API, файлы). База пирамиды тестов: много (сотни), быстро (миллисекунды), дёшево. Что проверяет: входную валидацию, бизнес-логику, обработку ошибок, пограничные случаи. Что не проверяет: производительность, безопасность (глубоко), совместимость. Изоляция (mock) — ключевое понятие: заглушки для БД, времени, внешних API. Покрытие кода (code coverage) >80% строк, >70% веток — хорошо. В CI/CD: запуск при каждом коммите, падение тестов → остановка деплоя. Хороший тест: понятное имя, одна проверка, независим, детерминирован, быстр. Типичные проблемы: хрупкие тесты (падают при косметических изменениях), flaky (нестабильные), медленные.

Введение: Проверка деталей

Представьте, что вы собираете сложный механизм из сотен деталей. Можно собрать всё сразу и надеяться, что заработает. А можно проверить каждую деталь отдельно: шестерёнку — на станке, пружину — на стенде, винт — калибром.

Модульное тестирование — это проверка деталей. В мире API это проверка одного метода, одной функции, одного класса в изоляции от остальной системы.

Модульное тестирование (Unit Testing) — это тестирование наименьших тестируемых частей приложения (методов, функций, классов) в изоляции от внешних зависимостей.

Для системного аналитика модульное тестирование — это не про написание тестов (это задача разработчиков). Это про понимание: что такое хороший модульный тест, какие требования к API можно проверить на этом уровне, как интерпретировать результаты тестов, как участвовать в ревью тестовой стратегии.

Модульные тесты — это первая линия обороны. Если они проходят, можно двигаться дальше. Если нет — интеграционное и нагрузочное тестирование бессмысленны, потому что базовая логика уже сломана.

Место модульного тестирования в пирамиде тестов

    graph TD
    A[E2E тесты<br>мало, медленные] --> B[Интеграционные тесты<br>средне, средние]
    B --> C[Модульные тесты<br>много, быстрые]
  
УровеньКоличествоСкоростьЧто проверяет
Модульные тестыМного (сотни/тысячи)МиллисекундыОдин метод, одна функция
Интеграционные тестыСредне (десятки)СекундыСвязь между компонентами
E2E тестыМало (единицы)МинутыВесь сценарий пользователя

Почему модульные тесты — основа пирамиды:

  • Их много (покрывают все ветки кода)
  • Они быстрые (можно запускать после каждого изменения)
  • Они дёшевы (не требуют развёртывания инфраструктуры)
  • Они точные (указывают на конкретную строку, где проблема)

Что тестируют модульные тесты API

Входная валидация

Что проверяетПример
Обязательные поляЗапрос без name возвращает 400
Типы данныхage должно быть числом
Диапазоныage от 0 до 150
Форматыemail соответствует формату
Длиныname не длиннее 100 символов
Списки значенийstatus только из [active, pending, blocked]

Бизнес-логика

Что проверяетПример
УсловияЕсли баланс < 0, возвращается ошибка
ВычисленияСкидка 10% применена правильно
СтатусыЗаказ со статусом “delivered” нельзя отменить
ПраваПользователь без прав не может удалить чужой заказ

Обработка ошибок

Что проверяетПример
Несуществующий ресурсGET /users/99999 → 404
КонфликтыСоздание пользователя с существующим email → 409
Ошибки валидацииНеправильный формат → 422
Ошибки авторизацииБез токена → 401
Ошибки правС токеном, но нет прав → 403

Пограничные случаи

Что проверяетПример
Пустые значенияname = ""
Null значенияage = null
Максимальные значенияname из 100 символов
Минимальные значенияage = 0
Пустые спискиitems = []

Изоляция: Что такое mock

Модульные тесты не должны зависеть от внешних систем. Для этого используют mock (заглушки).

Зависимости, которые нужно изолировать

ЗависимостьПочему нужно изолировать
База данныхМедленно, требует чистой базы для каждого теста
Внешние APIНестабильны, медленны, могут быть платными
Файловая системаСостояние между тестами, права доступа
ВремяТест должен работать одинаково в любое время
СлучайностьТест должен быть детерминированным

Пример: Проверка бизнес-логики без БД

Вместо реальной базы данных тест использует mock репозитория.

Без mock (плохо)С mock (хорошо)
Тест реально пишет в БДТест использует заглушку
Нужна чистая БД перед каждым тестомНет внешних зависимостей
Медленно (секунды)Быстро (миллисекунды)
Может упасть из-за проблем с БДПадает только из-за логики

Покрытие кода (Code Coverage)

Что это

Процент кода, который выполняется во время тестов.

ПоказательЧто значитРекомендация
Строки (Lines)Сколько строк кода выполнено> 80%
Ветки (Branches)Сколько условий проверено (if/else)> 70%
Функции (Functions)Сколько функций вызвано> 90%

Как интерпретировать покрытие

ПокрытиеЧто значит
90%+Отлично. Код хорошо протестирован
70-90%Хорошо. Есть небольшие пробелы
50-70%Средне. Есть риски
< 50%Плохо. Много непроверенного кода

Важное предупреждение: Высокое покрытие не гарантирует качество. Можно написать тест, который выполняет строку, но не проверяет результат. Покрытие — это количество, а не качество.

Что аналитик должен знать о модульных тестах

Какие требования можно проверить модульными тестами

Тип требованияМожно проверить?Пример
ФункциональныеДа“При переводе денег баланс уменьшается”
ВалидационныеДа“Email должен быть в правильном формате”
Бизнес-правилаДа“Скидка не более 50%”
Обработка ошибокДа“При неверном ID возвращается 404”
ПроизводительностьНетТребует нагрузочного тестирования
БезопасностьЧастичноSQL инъекции — да, DDoS — нет
СовместимостьНетТребует интеграционного тестирования
ДоступностьНетТребует инфраструктурного тестирования

Как читать результаты модульных тестов

TestCreateUser_Success PASSED (0.02s)
TestCreateUser_MissingName FAILED (0.01s)
    expected: 400 Bad Request
    actual:   200 OK
    user_test.go:45: response.StatusCode = 200, want 400

Что видит аналитик:

  • Какой тест упал (TestCreateUser_MissingName)
  • Почему упал (ожидал 400, получил 200)
  • Где искать проблему (user_test.go:45)

Какие вопросы задать команде

ВопросЗачем
“Какое у нас покрытие кода?”Понимать уровень риска
“Есть ли тесты на негативные сценарии?”Проверка обработки ошибок
“Как часто запускаются тесты?”В CI при каждом коммите? Раз в день?
“Что делают тесты, которые падают?”Новые баги или сломанные тесты?
“Есть ли тесты на граничные случаи?”Пустые значения, максимальные длины

Хорошие и плохие модульные тесты

Хороший тест (понимает аналитик)

Тест: Создание пользователя с валидными данными
Действие: POST /users с {name: "Иван", email: "ivan@example.com"}
Ожидание: 201 Created, тело содержит id и name
Результат: PASSED

Плохой тест (непонятен аналитику)

Тест: userCreateTest
Действие: someFunction("test", 123)
Ожидание: !null
Результат: PASSED

Признаки хорошего модульного теста:

ПризнакЧто значит
Понятное имяtestCreateUser_Success, а не test1
Одна проверкаТест проверяет одно поведение
НезависимостьНе зависит от других тестов
ДетерминизмВсегда даёт одинаковый результат
БыстротаВыполняется за миллисекунды

Модульные тесты и CI/CD

Где запускаются тесты

    graph LR
    A[Разработчик\nлокально] --> B[Git commit]
    B --> C[CI сервер\nзапуск тестов]
    C -->|Все тесты прошли| D[Деплой]
    C -->|Тесты упали| E[Остановка\nуведомление]
  

Что происходит при падении тестов

ДействиеКто делает
CI останавливает сборкуАвтоматически
Уведомление в чат (Slack, Teams)Автоматически
Автор коммита получает письмоАвтоматически
Аналитик видит в отчётеВ дашборде CI

Что должен знать аналитик о CI

  • Как часто падают тесты? (редко — хорошо, часто — плохо)
  • Сколько времени идут тесты? (минуты — хорошо, часы — плохо)
  • Кто чинит упавшие тесты? (команда разработки)
  • Есть ли отчёт о покрытии? (дашборд)

Связь модульных тестов с требованиями

Traceability (прослеживаемость)

Хорошая практика — связывать тесты с требованиями.

ТребованиеТесты
REQ-001: Пользователь может зарегистрироватьсяtestCreateUser_Success, testCreateUser_MissingEmail, testCreateUser_DuplicateEmail
REQ-002: Email должен быть уникаленtestCreateUser_DuplicateEmail

Что даёт аналитику:

  • Понимание, какие требования покрыты тестами
  • Быстрая оценка влияния изменения требования
  • Обоснование, почему тест нужен

Частые проблемы с модульными тестами

Проблема 1: Хрупкие тесты (Brittle tests)

Тесты падают при любом изменении кода, даже не влияющем на поведение.

Пример: Тест проверяет точное сообщение ошибки, а разработчик изменил текст.

Решение: Проверять код ошибки, а не сообщение.

Проблема 2: Медленные тесты

Модульные тесты, которые работают секунды (должны — миллисекунды).

Решение: Изолировать зависимости (БД, сеть, файлы).

Проблема 3: Flaky tests (нестабильные)

Тест то проходит, то падает без изменения кода.

Решение: Убрать случайность, зависимости от времени, порядка выполнения.

Проблема 4: Тестирование реализации, а не поведения

Тест проверяет, как метод делает, а не что делает.

Решение: Тестировать контракт (вход → выход), а не внутреннюю реализацию.

Резюме

  1. Модульное тестирование — проверка наименьших частей приложения (методов, функций) в изоляции. База пирамиды тестов.

  2. Что проверяет: входную валидацию, бизнес-логику, обработку ошибок, пограничные случаи.

  3. Что не проверяет: производительность, безопасность (полностью), совместимость, доступность.

  4. Изоляция (mock) — ключевое понятие. Тесты не должны зависеть от БД, сети, времени, случайности.

  5. Покрытие кода (coverage) — процент проверенного кода. >80% строк, >70% веток — хорошо. Но покрытие не гарантирует качество.

  6. Хороший тест: понятное имя, одна проверка, независим, детерминирован, быстр.

  7. В CI/CD: тесты запускаются при каждом коммите. Если тесты падают — деплой останавливается.

Проверка знаний

Вопрос 1 из 4
Что такое модульное тестирование?
Какое место модульные тесты занимают в пирамиде тестов?
Что в модульных тестах особенно важно изолировать?
Как в теме предлагается интерпретировать code coverage?

Вопросы, где были ошибки