pytest-container
Запуск тестов внутри образа как стадии сборки. Используется в уроке 6.12 и в разделе 15.
Что демонстрирует
- стадия
test, не входящая в финальный образ; - падение тестов останавливает сборку — тесты становятся обязательными;
- порог покрытия (
fail_under = 90) проверяется там же; - независимая стадия
lintсобирается параллельно со стадиейtest; - в финальном образе нет ни
pytest, ниruff, ни тестов.
Запуск
Финальный образ — стадия test при этом не выполняется:
docker build -t calc .
docker run --rm calc
Явный запуск тестов:
docker build --target test -t calc:test .
Явный запуск линтера:
docker build --target lint -t calc:lint .
Проверка: тесты блокируют сборку
Сломайте любой тест и убедитесь, что сборка падает:
sed -i 's/assert divide(a, b) == expected/assert divide(a, b) == 999/' tests/test_core.py
docker build --target test -t calc:test . ; echo "exit code: $?"
Ожидается ненулевой код и отчёт pytest с упавшим тестом.
Верните тест:
sed -i 's/assert divide(a, b) == 999/assert divide(a, b) == expected/' tests/test_core.py
Проверка: порог покрытия
Порог задан в pyproject.toml (fail_under = 90). Добавьте непокрытую функцию в src/calc/core.py и пересоберите стадию test — сборка упадёт по недобору покрытия, а не по упавшему тесту.
Проверка: финальный образ чист
docker run --rm --entrypoint sh calc -c 'command -v pytest || echo "pytest отсутствует"'
docker run --rm --entrypoint sh calc -c 'ls /app/tests 2>/dev/null || echo "тестов нет"'
docker run --rm --entrypoint id calc -u
Параллельность стадий
Стадии lint и test независимы — BuildKit собирает их одновременно:
docker build --no-cache --target lint -t calc:lint . &
docker build --no-cache --target test -t calc:test . &
wait
Локальный запуск без Docker
python -m venv .venv && . .venv/bin/activate
pip install -e . -r requirements-dev.txt
pytest --cov=calc
ruff check src/
Уборка
docker rmi calc calc:test calc:lint 2>/dev/null