Зачем нужны тесты и какими они бывают
Вы поправили одну функцию, выкатили обновление — а через день пользователь пишет, что сломалось что-то совсем другое, работавшее вчера. Правка в одном месте незаметно задела другое, и узнали вы об этом последним.
Тестирование нужно, чтобы узнавать о таких поломках сразу, а не от пользователей. Это набор проверок, которые подтверждают, что код делает то, что от него ждут, — и продолжает делать после каждого изменения.
Зачем тестировать
В 1999 году NASA потеряло марсианский аппарат стоимостью $125 миллионов из-за простой ошибки: в расчётах смешались разные системы единиц. Один тест мог выявить это до запуска.
Это крайний случай общего правила — чем позже найдена ошибка, тем дороже её исправить:
Кроме ранней ловли ошибок тесты дают ещё несколько вещей:
- уверенность при изменениях — с набором тестов можно смело рефакторить и добавлять новое: если что-то сломается, тесты скажут об этом первыми;
- живую документацию — тесты показывают, как код должен использоваться, и, в отличие от обычной документации, не устаревают;
- лучший дизайн — если функцию трудно протестировать, обычно она просто слишком сложно устроена;
- защиту от регрессий — так называют поломки уже работавшего кода после новых изменений; ровно их ловит пример ниже.
Типы тестов
Тесты различаются масштабом проверяемого — от одной функции до всей системы.
Модульные тесты (unit)
Проверяют самые маленькие, изолированные части программы — отдельные функции, методы или классы. Цель — убедиться, что каждый «кирпичик» кода работает правильно сам по себе.
Python 3.13def add(a, b): return a + b print(add(2, 3) == 5)True
Проверка вернула True — функция работает как ожидалось. Теперь представим, что кто-то поправил add и случайно задел логику:
Python 3.13def add(a, b): return a + b + 1 # случайно закравшаяся ошибка print(add(2, 3) == 5)False
Та же проверка мгновенно вернула False. Это и есть модульный тест: маленькая проверка, которая сама говорит, цела ли функция, — так и ловят регрессии. В следующей статье увидим, как оформлять такие проверки через assert и запускать их пачками.
Интеграционные тесты
Проверяют взаимодействие между несколькими модулями: например, что после создания пользователя в базе данных его распознаёт система входа. Модули могут быть исправны поодиночке и всё равно не сойтись друг с другом — это ловится именно здесь.
Сквозные тесты (E2E)
Проверяют систему целиком с точки зрения пользователя, проходя через все слои приложения. Пример — полный сценарий регистрации на сайте: заполнить форму, отправить данные, получить письмо, войти в систему.
Пирамида тестирования
Сколько каких тестов писать? Ответ обычно рисуют пирамидой:
Чем выше уровень, тем тесты медленнее и дороже в поддержке — поэтому их меньше. Основной объём составляют быстрые модульные тесты: они точно указывают на место поломки.
Принципы хорошего теста (FIRST)
Пять признаков полезного теста складываются в акроним FIRST:
- Fast — быстрый: медленные тесты запускают редко;
- Independent — независимый: не полагается на другие тесты и порядок запуска;
- Repeatable — повторяемый: даёт один и тот же результат при каждом запуске;
- Self-validating — самопроверяемый: сам говорит, прошёл или нет, без ручного сравнения результатов;
- Timely — своевременный: написан вместе с кодом, а не «когда-нибудь потом».
Фреймворки для тестирования
Писать проверки вручную, как в примере с add, быстро надоедает: нужен запуск пачкой, отчёты, удобные сравнения. Это работа фреймворков, и в Python их два основных:
- pytest — сторонний, с лаконичным синтаксисом; с него и начнём;
- unittest — встроенный в стандартную библиотеку.
Проверка понимания
Какое утверждение о тестировании программного обеспечения наиболее точно?
В следующей статье — практика с pytest: первые настоящие тесты, оператор assert и запуск пачкой.
