Зачем нужны тесты и какими они бывают

Вы поправили одну функцию, выкатили обновление — а через день пользователь пишет, что сломалось что-то совсем другое, работавшее вчера. Правка в одном месте незаметно задела другое, и узнали вы об этом последним.

Тестирование нужно, чтобы узнавать о таких поломках сразу, а не от пользователей. Это набор проверок, которые подтверждают, что код делает то, что от него ждут, — и продолжает делать после каждого изменения.

Зачем тестировать

В 1999 году NASA потеряло марсианский аппарат стоимостью $125 миллионов из-за простой ошибки: в расчётах смешались разные системы единиц. Один тест мог выявить это до запуска.

Это крайний случай общего правила — чем позже найдена ошибка, тем дороже её исправить:

Когда обнаружена ошибкаОтносительная стоимость исправления
Во время разработки1x
Во время код-ревью2x
Во время тестирования QA5x-10x
После выпуска (в продакшене)30x-100x+

Кроме ранней ловли ошибок тесты дают ещё несколько вещей:

  • уверенность при изменениях — с набором тестов можно смело рефакторить и добавлять новое: если что-то сломается, тесты скажут об этом первыми;
  • живую документацию — тесты показывают, как код должен использоваться, и, в отличие от обычной документации, не устаревают;
  • лучший дизайн — если функцию трудно протестировать, обычно она просто слишком сложно устроена;
  • защиту от регрессий — так называют поломки уже работавшего кода после новых изменений; ровно их ловит пример ниже.

Типы тестов

Тесты различаются масштабом проверяемого — от одной функции до всей системы.

Модульные тесты (unit)

Проверяют самые маленькие, изолированные части программы — отдельные функции, методы или классы. Цель — убедиться, что каждый «кирпичик» кода работает правильно сам по себе.

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

print(add(2, 3) == 5)
True

Проверка вернула True — функция работает как ожидалось. Теперь представим, что кто-то поправил add и случайно задел логику:

Python 3.13
def add(a, b):
    return a + b + 1        # случайно закравшаяся ошибка

print(add(2, 3) == 5)
False

Та же проверка мгновенно вернула False. Это и есть модульный тест: маленькая проверка, которая сама говорит, цела ли функция, — так и ловят регрессии. В следующей статье увидим, как оформлять такие проверки через assert и запускать их пачками.

Интеграционные тесты

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

Сквозные тесты (E2E)

Проверяют систему целиком с точки зрения пользователя, проходя через все слои приложения. Пример — полный сценарий регистрации на сайте: заполнить форму, отправить данные, получить письмо, войти в систему.

Пирамида тестирования

Сколько каких тестов писать? Ответ обычно рисуют пирамидой:

Сквозные (E2E)медленные · мало
Интеграционныесреднее
Модульные (unit)быстрые · много

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

Принципы хорошего теста (FIRST)

Пять признаков полезного теста складываются в акроним FIRST:

  • Fast — быстрый: медленные тесты запускают редко;
  • Independent — независимый: не полагается на другие тесты и порядок запуска;
  • Repeatable — повторяемый: даёт один и тот же результат при каждом запуске;
  • Self-validating — самопроверяемый: сам говорит, прошёл или нет, без ручного сравнения результатов;
  • Timely — своевременный: написан вместе с кодом, а не «когда-нибудь потом».

Фреймворки для тестирования

Писать проверки вручную, как в примере с add, быстро надоедает: нужен запуск пачкой, отчёты, удобные сравнения. Это работа фреймворков, и в Python их два основных:

  • pytest — сторонний, с лаконичным синтаксисом; с него и начнём;
  • unittest — встроенный в стандартную библиотеку.

Проверка понимания

Какое утверждение о тестировании программного обеспечения наиболее точно?

В следующей статье — практика с pytest: первые настоящие тесты, оператор assert и запуск пачкой.