Как организовать юнит-тесты в Python?
Что проверяют
Понимаете ли вы структуру и изоляцию тестов, а не только синтаксис проверки.
Как отвечать
В современных проектах берут pytest: простой синтаксис, фикстуры, параметризация, богатая экосистема плагинов.
Организация:
- структура: каталог
testsрядом с исходниками, файлыtest_*.py, функцииtest_*. Общие фикстуры вconftest.py, чтобы не импортировать их вручную; - один тест проверяет одно поведение, а имя описывает условие и ожидание:
test_returns_none_when_user_not_found; - три части внутри: подготовка, действие, проверка. Так тест читается за секунды;
- параметризация вместо копий:
@pytest.mark.parametrizeс набором входов и ожиданий, включая граничные значения; - фикстуры для подготовки и очистки, с областью видимости под стоимость ресурса;
- изоляция: никакой зависимости от порядка запуска и от общего состояния. Внешние вызовы подменяются (
unittest.mock,monkeypatch), время и случайность фиксируются, файлы пишутся вtmp_path; - база: либо подмена репозитория, либо реальная база в контейнере через
testcontainersс откатом транзакции после каждого теста.
Что стоит добавить про пользу и меру: покрытие показывает непроверенный код, но само по себе целью не является; медленные тесты помечают и выносят в отдельный прогон; тест на найденный дефект пишется до исправления, чтобы убедиться, что он воспроизводит проблему.
Где спотыкаются
Тестируют реализацию: подменяют внутренние функции и проверяют порядок вызовов, из-за чего тесты ломаются при любом рефакторинге.
Пишут один тест с десятью проверками, и при падении непонятно, что именно сломалось.
Оставляют зависимость от общей базы или от порядка запуска, и тесты начинают мигать.