Какие части кода важнее покрывать тестами
Что проверяют
Умеете ли вы распределять время на тесты по риску, а не гнаться за процентом покрытия. Следом обычно спрашивают, какое покрытие вы считаете нормальным и что делать с кодом, который тяжело тестировать.
Как отвечать
Отталкивайтесь от двух вопросов: что сломается дороже всего и что ломается чаще всего.
- бизнес-логика с деньгами и правами: расчёт цен, скидок, налогов, проверки доступа. Ошибка здесь стоит денег или утечки данных;
- ветвистый код: парсеры, валидация, функции с кучей условий. Граничные значения удобно гонять через
pytest.mark.parametrize, а случайные входы генерироватьhypothesis; - границы с внешним миром: HTTP-клиенты, база, очереди. Сеть подменяют через
unittest.mock,responsesилиrespx, а запросы к базе проверяют на настоящем PostgreSQL в testcontainers, потому что SQLite ведёт себя иначе; - обработка ошибок: таймауты, ретраи, пустые ответы, то, что руками почти не проверяют;
- места прошлых багов: каждый исправленный баг получает регрессионный тест;
- часто меняемые модули: по истории git видно, где правки идут каждую неделю, там регрессии вероятнее.
Можно не покрывать тривиальные геттеры, настройки, сгенерированные миграции Django и поведение самого фреймворка.
Покрытие меряют через pytest-cov с флагом --cov-branch, иначе строка с if считается покрытой, даже если одна из веток ни разу не выполнялась. Процент полезен как карта непокрытых мест, но не как цель. Проверить, ловят ли тесты ошибки, помогает мутационное тестирование, например mutmut.
Где спотыкаются
Ставят цель «100% покрытия». Появляются тесты на геттеры, а сложная логика проверена одним счастливым сценарием.
Мокают всё подряд, включая собственный код и базу. Тесты зелёные, а реальный запрос в проде падает.
Смешивают уровни: обещают «покрыть тестами» нагруженный модуль, хотя производительность проверяют отдельно, нагрузочным инструментом вроде Locust, а не юнит-тестами.