Перейти к основному контенту
Tech Path Finder
КурсыИнтервьюКод-ревьюБлог
Tech Path Finder

Персонализированный путеводитель в IT. Квизы, мок-интервью, код ревью и аналитика прогресса.

@potapov_me

Платформа

  • Курсы
  • Прогресс
  • Мок-интервью
  • Код ревью
  • Живое ревью с ИИ
  • Тренажёр переговоров
  • Закладки

Контент

  • Блог
  • Главная
  • Обратная связь

Компания

  • О проекте
  • Поддержать проект
  • Тарифы
  • Условия использования
  • Конфиденциальность
  • Согласие на обработку данных
  • Cookie
  • Реквизиты

Аккаунт

  • Войти
  • Зарегистрироваться
  • Профиль

© 2026 Tech Path Finder. Все права защищены.

·ИП Потапов К.С.·Политика конфиденциальности·
Сделано с ❤️ в России
  1. Тестируем классы и ошибки: pytest.raises
testing_classes_and_errors

Тестируем классы и ошибки: pytest.raises

Учимся проверять поведение классов, правильно ловить исключения через pytest.raises и убираем дублирование первым шагом к фикстурам

⚠️ Урок устарел. Свежая версия в курсе pytest-basics: Тестируем классы и ошибки.

#Зачем этот урок

✅ Пишем тесты для классов, а не только для функций
✅ Ловим ошибки правильно (pytest.raises вместо try/except)
✅ Показываем первую фикстуру, чтобы не копировать создание объекта

#Стартовый код

src/bank_account.py:

class BankAccount: def __init__(self): self.balance = 0 def deposit(self, amount): if amount <= 0: raise ValueError("Amount must be positive") self.balance += amount def withdraw(self, amount): if amount <= 0: raise ValueError("Amount must be positive") if amount > self.balance: raise ValueError("Not enough money") self.balance -= amount

Структура проекта:

bank-app/
  src/bank_account.py
  tests/test_bank_account.py

Запуск: PYTHONPATH=. pytest -v

#Базовые тесты класса

tests/test_bank_account.py:

from src.bank_account import BankAccount def test_deposit_increases_balance(): account = BankAccount() account.deposit(200) assert account.balance == 200 def test_withdraw_reduces_balance(): account = BankAccount() account.deposit(150) account.withdraw(50) assert account.balance == 100

#Проверяем ошибки через pytest.raises

import pytest from src.bank_account import BankAccount def test_cannot_withdraw_more_than_balance(): account = BankAccount() account.deposit(100) with pytest.raises(ValueError, match="Not enough money"): account.withdraw(150) # ✅ Тест пройдёт только если исключение поднято # ✅ match страхует от «другого» ValueError def test_amount_must_be_positive_on_deposit(): account = BankAccount() with pytest.raises(ValueError, match="positive"): account.deposit(0)

Почему так лучше, чем try/except: тест падает, если исключение не поднято, и сообщение контролируемо (match), без ручного assert False.

#Убираем дублирование первой фикстурой

Проблема до фикстуры: одно и то же создание объекта в каждом тесте.

# ДО: дублирование def test_deposit_increases_balance(): account = BankAccount() # повторяется account.deposit(200) assert account.balance == 200 def test_withdraw_reduces_balance(): account = BankAccount() # повторяется account.deposit(150) account.withdraw(50) assert account.balance == 100

Решение: вынести создание в фикстуру.

import pytest from src.bank_account import BankAccount @pytest.fixture def account(): return BankAccount() def test_deposit_increases_balance(account): account.deposit(200) assert account.balance == 200 def test_cannot_withdraw_more_than_balance(account): account.deposit(100) with pytest.raises(ValueError, match="Not enough money"): account.withdraw(150)

На этом этапе фикстура — всего лишь функция, которая создаёт объект. В следующем уроке углубимся в скоупы и cleanup.

#Практика: TaskManager с ошибками

src/task_manager.py:

class TaskManager: def __init__(self): self.tasks = [] def add(self, text: str): cleaned = text.strip() if not cleaned: raise ValueError("Task cannot be empty") self.tasks.append(cleaned) return cleaned def all(self) -> list[str]: return self.tasks # специально без copy, чтобы тест подсветил проблему

Задание:

  1. Напишите фикстуру empty_manager(), возвращающую новый TaskManager.
  2. Тесты:
  • test_add_trims_whitespace — пробелы обрезаются.
  • test_empty_task_raises_error — проверка через pytest.raises и match="empty".
  • test_all_returns_copy — после внешней модификации возвращённого списка all() должен вернуть исходные данные. Код сейчас без copy — тест покажет баг.

#Чеклист

  • Один класс — несколько коротких тестов на разные ветки.
  • Ошибки проверяются через pytest.raises, при необходимости с match.
  • Повторяющийся setup вынесен в простую фикстуру.
  • Запуск PYTHONPATH=. pytest -v зелёный.

Дальше — полноценные фикстуры, параметризация и маркеры. А пока убедитесь, что ошибки покрыты, а код читается без try/except в тестах. 🎯

Далее: Тесты, которые ловят реальные баги