Отладка: понимаем ошибки
Вы уже написали 50 тестов. 3 из них падают. Как быстро найти причину и исправить?
Цель: Научиться эффективно отлаживать тесты и читать сообщения об ошибках pytest.
Вы точно готовы?
Убедитесь, что умеете:
# Писать тесты с фикстурами
@pytest.fixture
def account():
return BankAccount(100)
def test_deposit(account):
account.deposit(50)
assert account.balance == 150Если фикстуры непонятны — вернитесь к уроку 6.
Проблема: непонятные ошибки
Минимальный вывод по умолчанию
pytestРезультат:
============================= test session starts ==============================
collected 10 items
tests/test_bank.py .F.F..... [100%]
=================================== FAILURES ===================================
_______________________________ test_withdraw __________________________________
def test_withdraw():
> account.withdraw(200)
E ValueError: Insufficient funds
tests/test_bank.py:25: ValueError
============================== short test summary info ==========================
FAILED tests/test_bank.py::test_withdraw
========================== 1 failed, 9 passed in 0.12s =========================Проблемы:
- ❌ Не видно какие именно тесты прошли
- ❌ Мало информации об ошибке
- ❌ Не понятно с какими данными упал тест
Решение 1: Verbose режимы
pytest -v: показать все тесты
pytest -vРезультат:
============================= test session starts ==============================
collected 10 items
tests/test_bank.py::test_create_account PASSED [ 10%]
tests/test_bank.py::test_deposit PASSED [ 20%]
tests/test_bank.py::test_withdraw FAILED [ 30%]
tests/test_bank.py::test_multiple_deposits PASSED [ 40%]
tests/test_bank.py::test_negative_balance PASSED [ 50%]
...✅ Теперь видно каждый тест!
pytest -vv: ещё больше деталей
pytest -vvРезультат для параметризованных тестов:
tests/test_bank.py::test_deposit[100-50-150] PASSED [ 20%]
tests/test_bank.py::test_deposit[0-100-100] PASSED [ 40%]
tests/test_bank.py::test_deposit[1000-1-1001] PASSED [ 60%]✅ Показывает параметры для каждого теста!
Когда использовать
# Разработка: хочу видеть все тесты
pytest -v
# Отладка параметризованных тестов: какой именно кейс упал?
pytest -vv
# CI/CD: минимальный вывод для скорости
pytestРешение 2: Stacktrace опции
--tb=short: короткий stacktrace
pytest --tb=shortРезультат:
=================================== FAILURES ===================================
_______________________________ test_withdraw __________________________________
tests/test_bank.py:25: in test_withdraw
account.withdraw(200)
E ValueError: Insufficient funds✅ Только суть ошибки (без полного stacktrace)
--tb=long: полный stacktrace
pytest --tb=longРезультат:
=================================== FAILURES ===================================
_______________________________ test_withdraw __________________________________
@pytest.fixture
def account():
> return BankAccount(100)
tests/conftest.py:12:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
def __init__(self, balance):
if balance < 0:
> raise ValueError("Balance cannot be negative")
E ValueError: Balance cannot be negative
src/bank_account.py:8: ValueError✅ Полный путь ошибки (где именно она произошла)
--tb=no: без stacktrace
pytest --tb=noРезультат:
tests/test_bank.py F
========================== short test summary info ==========================
FAILED tests/test_bank.py::test_withdraw
========================== 1 failed, 9 passed in 0.12s =========================✅ Только статистика (для CI когда логи большие)
Stacktrace опции
--tb=auto # Автоматический (default)
--tb=long # Полный stacktrace
--tb=short # Короткий stacktrace
--tb=line # Только строка с ошибкой
--tb=native # Python stacktrace
--tb=no # Без stacktraceРекомендации:
--tb=short— для большинства случаев ✅--tb=long— когда ошибка глубоко в коде--tb=no— для CI (чтобы логи не раздувались)
Решение 3: Интерактивная отладка --pdb
Что такое pdb?
pdb (Python Debugger) — встроенный отладчик Python, который позволяет:
- 🔍 Остановить программу в нужном месте
- 🔬 Изучить значения переменных
- 🎯 Выполнить код прямо во время работы программы
- 🐛 Найти причину бага без перезапуска тестов
Зачем это нужно?
Вместо того чтобы добавлять print() и перезапускать тест 10 раз, вы:
- Запускаете тест один раз
- pytest останавливается на ошибке
- Вы исследуете всё что угодно прямо в момент падения
Это как поставить видео на паузу и рассмотреть кадр в деталях! 🎬
pytest --pdb: остановка на ошибке
pytest --pdbЧто происходит:
- Тест падает
- pytest останавливается ровно на месте ошибки
- Открывается интерактивная консоль Python (pdb)
Результат:
=================================== FAILURES ===================================
_______________________________ test_withdraw __________________________________
def test_withdraw():
> account.withdraw(200)
tests/test_bank.py:25
(Pdb) _✅ Вы внутри теста! Можете проверить что угодно прямо здесь и сейчас.
Команды pdb
В интерактивной консоли:
(Pdb) account.balance
100
(Pdb) account
<BankAccount object at 0x...>
(Pdb) print(account.balance)
100
(Pdb) account.deposit(50)
(Pdb) account.balance
150
(Pdb) c # Continue (продолжить выполнение)
(Pdb) q # Quit (выйти из pdb)Практический пример
def test_complex_calculation():
"""Тест сложного расчёта"""
user = User("Alice", 25)
account = BankAccount(1000)
# Что-то пошло не так...
result = calculate_interest(account, user.age)
assert result == 50 # FAILEDЗапускаем с --pdb:
pytest tests/test_bank.py::test_complex_calculation --pdbВ pdb:
(Pdb) result
45 # Ага, получили 45 вместо 50!
(Pdb) account.balance
1000
(Pdb) user.age
25
(Pdb) calculate_interest(account, 30)
60 # Для возраста 30 получается 60
(Pdb) calculate_interest(account, 25)
45 # Для возраста 25 получается 45 (не 50!)
# Нашли баг в тесте! Expected должно быть 45, не 50pytest --pdbcls для улучшенной отладки
Используйте ipdb (IPython debugger) для подсветки:
# Установите ipdb
pip install ipdb
# Используйте вместо обычного pdb
pytest --pdb --pdbcls=IPython.terminal.debugger:TerminalPdb✅ Получите автодополнение и подсветку синтаксиса!
Решение 4: Smart test execution
--lf: запустить только упавшие тесты
# Первый запуск: 10 тестов, 3 упали
pytest
# Повторный запуск: только 3 упавших теста
pytest --lf # last-failedРезультат:
============================= test session starts ==============================
run-last-failure: rerun previous 3 failures
collected 10 items / 7 deselected / 3 selected
tests/test_bank.py::test_withdraw PASSED [ 33%]
tests/test_bank.py::test_negative PASSED [ 66%]
tests/test_bank.py::test_overdraft PASSED [100%]
===================== 3 passed, 7 deselected in 0.05s =======================✅ Быстро проверяем что баги исправлены!
--ff: сначала упавшие, потом остальные
pytest --ff # failed-firstПорядок запуска:
- Сначала 3 упавших теста
- Потом 7 прошедших тестов
✅ Ускоряет обнаружение новых ошибок!
-x: остановка на первой ошибке
pytest -x # stop on first failureРезультат:
tests/test_bank.py::test_deposit PASSED [ 10%]
tests/test_bank.py::test_withdraw FAILED [ 20%]
=================================== FAILURES ===================================
...
!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
========================== 1 failed, 1 passed in 0.05s =========================✅ Не тратим время на остальные тесты — сразу чиним первую ошибку!
--maxfail=N: остановка после N ошибок
pytest --maxfail=3 # Остановиться после 3 ошибок✅ Баланс между -x (слишком рано) и без флага (слишком поздно)
Комбинирование опций (практика)
Сценарий 1: Быстрая отладка одного теста
# Запускаем только упавший тест с отладкой
pytest tests/test_bank.py::test_withdraw -vv --pdbРезультат:
-vv— видим все детали теста--pdb— останавливаемся на ошибке для исследования
Сценарий 2: Исправляем упавшие тесты
# 1. Запускаем все тесты
pytest
# 2. Смотрим только упавшие
pytest --lf -vv
# 3. Отлаживаем первый упавший
pytest --lf -x --pdb
# 4. После исправления — снова только упавшие
pytest --lfСценарий 3: CI/CD режим
# Минимальный вывод, без stacktrace, стоп на первой ошибке
pytest --tb=no -x -qРезультат:
.........F
========================== short test summary info ==========================
FAILED tests/test_bank.py::test_withdraw
!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 9 passed in 0.12sСценарий 4: Отладка параметризованных тестов
# Какой именно параметр упал?
pytest -vv --tb=short
# Отладка конкретного параметра
pytest "tests/test_bank.py::test_deposit[100-50-150]" --pdbЧитаем сообщения об ошибках
AssertionError: простая ошибка
=================================== FAILURES ===================================
_______________________________ test_deposit ___________________________________
def test_deposit():
account = BankAccount(100)
account.deposit(50)
> assert account.balance == 200
E assert 150 == 200
E + where 150 = <BankAccount object at 0x...>.balance
tests/test_bank.py:15: AssertionErrorКак читать:
>— строка с ошибкойE assert 150 == 200— что пошло не так (получили 150, ожидали 200)E + where— откуда взялось значение 150
Решение: Ожидаемое значение неверно — должно быть 150, не 200.
ValueError: исключение в коде
=================================== FAILURES ===================================
_______________________________ test_withdraw __________________________________
def test_withdraw():
account = BankAccount(100)
> account.withdraw(200)
tests/test_bank.py:25:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
def withdraw(self, amount):
if amount > self.balance:
> raise ValueError("Insufficient funds")
E ValueError: Insufficient funds
src/bank_account.py:32: ValueErrorКак читать:
>— вызвалиaccount.withdraw(200)в тесте_ _ _— входим в методwithdrawE ValueError— код выбросил исключение
Решение: Тест пытается снять 200, но на счёте только 100 — это корректное поведение! Нужно либо изменить тест, либо добавить pytest.raises.
AttributeError: метод не найден
=================================== FAILURES ===================================
_______________________________ test_balance ___________________________________
def test_balance():
account = BankAccount(100)
> assert account.get_balance() == 100
E AttributeError: 'BankAccount' object has no attribute 'get_balance'
tests/test_bank.py:10: AttributeErrorРешение:
- Опечатка в названии метода (
get_balanceвместоbalance) - Или метод не реализован
TypeError: неправильные аргументы
=================================== FAILURES ===================================
_______________________________ test_deposit ___________________________________
def test_deposit():
account = BankAccount(100)
> account.deposit()
E TypeError: deposit() missing 1 required positional argument: 'amount'
tests/test_bank.py:15: TypeErrorРешение: Забыли передать аргумент amount.
Практический workflow отладки
Шаг 1: Запустите тесты
pytest -vШаг 2: Если упали — смотрим детали
pytest --lf -vv --tb=shortШаг 3: Отлаживаем интерактивно
pytest --lf -x --pdbВ pdb:
(Pdb) # Исследуем переменные
(Pdb) print(account.balance)
(Pdb) print(result)
(Pdb) # Пробуем разные значения
(Pdb) account.deposit(50)
(Pdb) account.balance
(Pdb) q # ВыходимШаг 4: Исправляем код
# Исправили баг в src/bank_account.py или тестеШаг 5: Проверяем исправление
pytest --lf # Запускаем только упавшие тестыШаг 6: Запускаем все тесты
pytest # Убеждаемся что ничего не сломалиЧто вы изучили
- pytest -v/-vv — детальный вывод тестов
- --tb опции — контроль stacktrace
- --pdb — интерактивная отладка
- --lf/--ff — умное переиспользование упавших тестов
- -x/--maxfail — ранняя остановка
- Чтение ошибок — AssertionError, ValueError, TypeError
- Практический workflow — от ошибки до исправления
Поздравляю! 🎉
Вы завершили курс pytest-basics! Теперь вы умеете:
- Писать базовые тесты (урок 0)
- Организовывать тесты в проекте (урок 1)
- Тестировать классы и ошибки (урок 2)
- Писать чистые тесты с AAA-паттерном (урок 3)
- Параметризовать тесты (урок 4)
- Группировать тесты маркерами (урок 5)
- Использовать фикстуры (урок 6)
- Отлаживать тесты (урок 7)
Что дальше?
Вы прошли Level 1: Beginner курс. Теперь переходите к более продвинутым темам:
- Level 2: pytest-junior — Mock'и, patching, fixtures advanced
- Level 3: pytest-professional-tools — Plugins, custom markers, hooks
- Level 4+: Async тестирование, property-based testing, performance тесты
Продолжайте практиковаться! Пишите тесты для своих проектов и применяйте изученные техники.
Устранение неисправностей
Запускайте pytest с флагом --pdb (не -pdb), например pytest --pdb
Проверьте:
- Используйте полную форму флага: pytest --pdb
- Для ipdb добавьте --pdbcls=IPython.terminal.debugger:TerminalPdb
Все тесты прошли в прошлый раз — сначала запустите обычный pytest, чтобы зафиксировать падения
Проверьте:
- Сделайте полный запуск: pytest
- Если не было падений, --lf ничего не запустит
Используйте минимальный вывод: pytest --tb=no -q
Проверьте:
- Уберите -v/-vv, оставьте тихий режим -q
- Сократите stacktrace флагом --tb=no или --tb=short
Сфокусируйтесь на ключевых строках и упростите вывод
Проверьте:
- Читайте снизу вверх — последняя ошибка важнее всего
- Ищите строку со знаком > (место, где упал тест)
- Запустите с --tb=short для компактного трейсбека
Используйте простую отладку через print() и запуск pytest -s, чтобы увидеть вывод
Проверьте:
- Добавьте print для ключевых переменных
- Запускайте с pytest -s, чтобы вывод не скрывался
🎉 Congratulations on completing the course!
Share your experience and get a promo code for free access to any premium material of your choice
Free access to any premium material of your choice
Your experience will help other students
Promo code will arrive via email within 24 hours