Покрытие кода и CI
Тесты, которые лежат в репозитории и не запускаются, ничего не гарантируют. Чтобы тесты реально работали, нужны две вещи:
- Покрытие: знать, какие части кода тесты действительно прогоняют, а какие нет.
- CI (Continuous Integration): чтобы тесты автоматически запускались на каждый push, а не «когда-нибудь» вручную.
Покрытие через pytest-cov
Покрытие это процент исходного кода, который выполнился во время прогона тестов. Высокое покрытие не гарантирует отсутствия ошибок (можно покрыть строку, но проверить её плохо), но низкое покрытие однозначно показывает «здесь не тестировано».
Для проектов с pytest стандартный инструмент это плагин pytest-cov:
pip install pytest pytest-cov
Запускаем с флагом --cov и именем пакета:
pytest --cov=app tests/
В выводе появится сводка:
---------- coverage: platform ... -- Python ... ----------- Name Stmts Miss Cover ------------------------------------- app/users.py 25 5 80% app/orders.py 18 0 100% ------------------------------------- TOTAL 43 5 88%
- Stmts: сколько строк кода в файле.
- Miss: сколько не выполнилось во время тестов.
- Cover: процент покрытия.
Для детального отчёта (с подсветкой непокрытых строк в браузере) используют HTML:
pytest --cov=app --cov-report=html
Команда создаст папку htmlcov/, откройте htmlcov/index.html — увидите построчное покрытие.
О процентах
Гнаться за 100% обычно не стоит. Реалистичная цель: 80-90% для бизнес-логики. То, что осталось непокрытым, важнее анализировать вручную: «это критичный код или просто getter/setter?». Покрытие это сигнал о пробелах, а не цель сама по себе.
Что такое CI
CI это автоматический запуск тестов (и любых других проверок) при изменениях в репозитории. На GitHub это делает GitHub Actions, в GitLab встроенный GitLab CI, есть ещё CircleCI, Jenkins и другие. Принцип у всех один: на каждый git push запускается заданный pipeline.

Минимальный CI на GitHub Actions
Положите файл .github/workflows/tests.yml в корне репозитория:
name: tests on: push: branches: [main] pull_request: branches: [main] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: '3.12' - run: pip install -r requirements.txt - run: pytest --cov=app
Что это делает:
- Запускается на push и pull_request в ветку main.
- Клонирует репозиторий и ставит Python 3.12.
- Ставит зависимости из requirements.txt.
- Запускает тесты с покрытием.
Если хоть один тест упал, pipeline становится красным, и PR блокируется (если включить такое требование в настройках репозитория).
Что это даёт
- Ошибка ловится сразу, а не через неделю в production.
- Тесты прогоняются всегда, не нужно полагаться на «я запущу локально перед мержем».
- Pull request показывает, прошли тесты или нет, ревьюер видит сразу.
- Покрытие отслеживается: если в PR добавлен код без тестов, видно в отчёте.
Проверка понимания
Что верно про покрытие кода и CI?
На этом модуль по тестированию закончен. Вы умеете писать тесты на pytest, использовать фикстуры и параметризацию, подменять зависимости моками, читать unittest-код в legacy-проектах, мерить покрытие и настраивать CI. Этого достаточно, чтобы вкатиться в любой production-проект — остальное добирается по месту.
