Иногда, когда только начинаешь разбираться в автоматизации тестирования, сталкиваешься с термином “юнит-тесты” и думаешь: «Ну это же для разработчиков». Часто так и есть. Но понимать юнит-тесты важно не только девелоперам. Это основа автоматизированного контроля качества, и если ты в тестировании или менеджменте — это как знание алфавита перед чтением.
Давай разберёмся, что это такое, зачем они нужны, и как устроены.
Юнит-тест — это автоматический тест, проверяющий работу одной минимальной логической единицы программы (юнита). Обычно под юнитом понимают одну функцию, метод или класс.
Простой пример:
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 / Автоматизаторы | Пользователь логинится, покупает товар |
Что делает юнит-тест «правильным»
- Изолированность. Он не зависит от других функций, базы данных или API. Всё, что вне его юнита, должно быть подделано (мокнуто).
- Повторяемость. Запускай хоть тысячу раз — результат должен быть один.
- Простота. Легко читается, легко поддерживается.
- Быстрота. Юнит-тесты должны быть быстрыми — иначе их не будут запускать при каждом коммите.
На 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. Это уже интеграционный тест.
- Тест ломается при любом изменении. Значит, он слишком чувствительный — например, жёстко привязан к форматированию строки.
Как внедрить юнит-тесты в команду
- Согласовать подход. Выберите язык, фреймворк, структуру тестов.
- Начать с нового кода. Не переписывайте старое — начните покрывать новый функционал.
- Включить тесты в CI/CD. Тесты должны запускаться автоматически при каждом коммите.
- Оценивать PR по тестам. Нет тестов — нет слияния. Если сложно протестировать — пусть объяснят, почему.
Юнит-тесты — это страховка, документация, и способ сказать “я уважаю труд команды, поэтому не ломаю ничего по мелочи”. Они не спасут от всех багов, но дают устойчивость при росте продукта.
Хороший юнит-тест не просто проверяет поведение — он даёт уверенность, что завтра ты сможешь переписать часть логики и не сломать остальное. А это — уже половина успеха.