Зачем нужны юнит-тесты
Зачем тратить время на тесты, если код «и так работает».
Юнит-тест — это маленькая программа, которая автоматически проверяет, что один небольшой кусок вашего кода (функция, метод, класс) ведёт себя так, как задумано.
Проблема, которую решают тесты
Представьте функцию, которая считает скидку. Вы написали её, проверили руками на паре чисел — работает. Через месяц коллега меняет соседний код, и ваша функция тихо ломается на отрицательных значениях. Никто этого не замечает, пока баг не доходит до пользователя.
Ручная проверка не масштабируется: нельзя каждый раз перепроверять весь проект вручную. Юнит-тесты делают это за вас — за доли секунды и при каждом запуске.
Что даёт автоматическое тестирование
- Раннее обнаружение ошибок. Баг находится в момент написания, а не в проде.
- Защита при изменениях (регрессии). Меняете код — гоняете тесты — сразу видите, что сломалось.
- Документация поведения. Тест показывает, как функция должна работать, на конкретных примерах.
- Уверенность в рефакторинге. Можно смело переписывать внутренности, пока тесты зелёные.
Пирамида тестов: где здесь юнит-тесты
Тесты делят по охвату:
| Вид | Что проверяет | Скорость |
| Юнит-тест | одну функцию/класс в изоляции | очень быстрый |
| Интеграционный | взаимодействие нескольких модулей | средний |
| End-to-end (E2E) | всю систему глазами пользователя | медленный |
Юнит-тестов должно быть больше всего: они дешёвые, быстрые и точно указывают на место поломки. Это основание «пирамиды тестирования».
Первое впечатление: тест — это просто проверка
По сути юнит-тест — это вызов вашей функции и сравнение результата с ожидаемым. Вот идея на чистом Python, без всяких библиотек:
def add(a, b):
return a + b
# «Ручной» тест: проверяем ожидание
result = add(2, 3)
assert result == 5, f"Ожидали 5, получили {result}"
print("Тест пройден: add(2, 3) == 5")
Вывод:
Тест пройден: add(2, 3) == 5
Оператор assert молча проходит, если условие истинно, и падает с ошибкой, если ложно. Это зерно любого теста. Но писать так руками неудобно: нет красивого отчёта, нет группировки, тяжело понять, сколько проверок прошло. Для этого и существует модуль unittest, с которого начнётся следующий урок.
Тесты экономят время, а не тратят его
Главное возражение против тестов: «нет времени, надо писать фичи». Но это ложная экономия. Без тестов время уходит на ручную проверку после каждого изменения, на отладку багов, которые всплыли в проде, и на страх трогать чужой код. Тесты переносят эти затраты в начало — вы тратите минуты на тест сейчас, чтобы не потерять часы на отладку потом. Чем дольше живёт проект, тем заметнее эта экономия: тесты окупаются многократно на каждом рефакторинге и каждой новой фиче.
Есть и психологический эффект. Зелёный набор тестов даёт уверенность: можно смело менять код, потому что любая поломка тут же станет видна. Без этой страховки разработчики обрастают «суеверным» кодом, который боятся трогать, и проект костенеет. Тесты возвращают свободу менять.
Когда тесты особенно окупаются
Не каждая строчка требует теста. Но тесты почти всегда оправданы для:
- бизнес-логики (расчёты, правила, форматирование);
- кода, который часто меняется;
- мест, где уже находили баги (тест-«ловушка», чтобы баг не вернулся);
- публичного API библиотеки или модуля.
Итог
- Юнит-тест автоматически проверяет маленький кусок кода в изоляции.
- Тесты ловят регрессии, документируют поведение и дают свободу рефакторинга.
- Юнит-тесты — основание пирамиды: их много, они быстрые.
- В основе любого теста — сравнение фактического результата с ожидаемым.