IT, менеджмент, тестирование, осознанность — блог Игоря Колосова

Игорь Колосов

Основы юнит-тестирования: зачем, как и с чего начать

Иногда, когда только начинаешь разбираться в автоматизации тестирования, сталкиваешься с термином “юнит-тесты” и думаешь: «Ну это же для разработчиков». Часто так и есть. Но понимать юнит-тесты важно не только девелоперам. Это основа автоматизированного контроля качества, и если ты в тестировании или менеджменте — это как знание алфавита перед чтением.

Давай разберёмся, что это такое, зачем они нужны, и как устроены.

Юнит-тест — это автоматический тест, проверяющий работу одной минимальной логической единицы программы (юнита). Обычно под юнитом понимают одну функцию, метод или класс.

Простой пример:

def add(a, b):
    return a + b

Юнит-тест:

def test_add():
    assert add(2, 3) == 5

Задача — проверить, что add(2, 3) всегда вернёт 5. Если кто-то в будущем добавит в add() проверку на отрицательные числа и случайно сломает обычное сложение — тест упадёт и сообщит об этом. Юнит-тесты позволяют:

  • Раннее обнаружение ошибок. Юнит-тесты запускаются при каждом коммите. Если сломали даже мелочь — вы узнаете сразу.
  • Упрощают рефакторинг. Когда у тебя есть покрытие тестами, ты можешь переписывать код без страха, что всё развалится.
  • Документируют поведение кода. По тестам легко понять, как должна работать функция.
  • Снижают стоимость багов. Исправить ошибку, найденную на этапе коммита, в 10 раз дешевле, чем после релиза.
Вид тестирования Что проверяет Кто пишет Примеры
Юнит-тесты Отдельную функцию / метод Разработчики add(), validate_email()
Интеграционные Как компоненты работают вместе Девы / QA API + БД, UI + бекенд
E2E Поведение всей системы через UI/API QA / Автоматизаторы Пользователь логинится, покупает товар

Что делает юнит-тест «правильным»

  1. Изолированность. Он не зависит от других функций, базы данных или API. Всё, что вне его юнита, должно быть подделано (мокнуто).
  2. Повторяемость. Запускай хоть тысячу раз — результат должен быть один.
  3. Простота. Легко читается, легко поддерживается.
  4. Быстрота. Юнит-тесты должны быть быстрыми — иначе их не будут запускать при каждом коммите.

На Python обычно используют pytest или unittest, на Java — JUnit, на JS — Jest или Mocha.

Пример на Python с pytest:

def is_even(n):
    return n % 2 == 0

def test_is_even():
    assert is_even(2) is True
    assert is_even(3) is False

Пример с моками:

from unittest.mock import Mock

def send_email(user, email_service):
    if user.is_active:
        email_service.send(user.email)

def test_send_email():
    mock_service = Mock()
    user = Mock()
    user.is_active = True
    user.email = 'test@example.com'

    send_email(user, mock_service)

    mock_service.send.assert_called_once_with('test@example.com')

Сколько юнитов нужно покрыть тестами? Ответ: столько, чтобы быть уверенным, что при изменении кода упадут тесты, если что-то пойдёт не так.

Инструменты покрытия (coverage.py, Istanbul, JaCoCo) покажут процент покрытия. Но 100% — не всегда цель. Главное — покрыть критически важную логику.

Антипаттерны

  • Тест ради теста. Проверка 1 + 1 == 2 — не тест, если в коде нет функции, которую это проверяет.
  • Тест зависит от БД или API. Это уже интеграционный тест.
  • Тест ломается при любом изменении. Значит, он слишком чувствительный — например, жёстко привязан к форматированию строки.

Как внедрить юнит-тесты в команду

  1. Согласовать подход. Выберите язык, фреймворк, структуру тестов.
  2. Начать с нового кода. Не переписывайте старое — начните покрывать новый функционал.
  3. Включить тесты в CI/CD. Тесты должны запускаться автоматически при каждом коммите.
  4. Оценивать PR по тестам. Нет тестов — нет слияния. Если сложно протестировать — пусть объяснят, почему.

Юнит-тесты — это страховка, документация, и способ сказать “я уважаю труд команды, поэтому не ломаю ничего по мелочи”. Они не спасут от всех багов, но дают устойчивость при росте продукта.

Хороший юнит-тест не просто проверяет поведение — он даёт уверенность, что завтра ты сможешь переписать часть логики и не сломать остальное. А это — уже половина успеха.