Отладка: понимаем ошибки
Вы уже написали 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, чтобы вывод не скрывался
🎉 Поздравляем с завершением курса!
Поделитесь опытом и получите промокод на бесплатный доступ к любому premium-материалу на выбор
Бесплатный доступ к любому premium-материалу на выбор
Ваш опыт поможет другим студентам
Промокод придет на email в течение 24 часов