← Назад к вопросам

Как вызывать принудительное исключение в негативных тестах?

2.0 Middle🔥 201 комментариев
#Python Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как вызывать принудительное исключение в негативных тестах?

В тестировании существует несколько подходов для проверки того, что ваш код корректно обрабатывает исключения. Рассмотрим самые эффективные методы.

1. Использование pytest.raises

Это основной и рекомендуемый способ в pytest:

import pytest

def divide(a, b):
    if b == 0:
        raise ZeroDivisionError("Деление на ноль")
    return a / b

def test_divide_by_zero():
    # Проверяем, что вызывается именно ZeroDivisionError
    with pytest.raises(ZeroDivisionError):
        divide(10, 0)

def test_divide_with_message():
    # Проверяем сообщение об ошибке
    with pytest.raises(ZeroDivisionError, match="Деление на ноль"):
        divide(10, 0)

def test_divide_with_exception_info():
    # Получаем объект исключения для дополнительной проверки
    with pytest.raises(ZeroDivisionError) as exc_info:
        divide(10, 0)
    assert "ноль" in str(exc_info.value)

2. Проверка иерархии исключений

Если нужно проверить, что выбрасывается исключение из определённой иерархии:

class CustomError(Exception):
    pass

class ValidationError(CustomError):
    pass

def validate_email(email):
    if "@" not in email:
        raise ValidationError("Неверный формат email")

def test_validation():
    # Работает для CustomError и его подклассов
    with pytest.raises(CustomError):
        validate_email("invalid_email")
    
    # Проверка точного типа
    with pytest.raises(ValidationError):
        validate_email("invalid_email")

3. Использование mock для принудительного вызова исключений

Когда нужно заставить внешнюю функцию выбросить исключение:

from unittest.mock import Mock, patch
import pytest

def fetch_user(user_id):
    """Функция, которая зависит от внешнего API"""
    response = requests.get(f"https://api.example.com/users/{user_id}")
    return response.json()

def test_fetch_user_connection_error():
    # Mock заставляет функцию выбросить исключение
    with patch(requests.get) as mock_get:
        mock_get.side_effect = ConnectionError("Нет соединения")
        
        with pytest.raises(ConnectionError):
            fetch_user(1)

4. Параметризованные тесты для проверки разных ошибок

import pytest

class Calculator:
    def divide(self, a, b):
        if not isinstance(a, (int, float)):
            raise TypeError("a должно быть числом")
        if not isinstance(b, (int, float)):
            raise TypeError("b должно быть числом")
        if b == 0:
            raise ZeroDivisionError("Деление на ноль")
        return a / b

@pytest.mark.parametrize("a,b,exc_type", [
    (10, 0, ZeroDivisionError),
    ("abc", 5, TypeError),
    (5, "xyz", TypeError),
])
def test_calculator_errors(a, b, exc_type):
    calc = Calculator()
    with pytest.raises(exc_type):
        calc.divide(a, b)

5. Проверка множественных исключений

def test_multiple_exceptions():
    # Проверяем, что один из типов исключений был выброшен
    with pytest.raises((ValueError, TypeError)):
        some_function()

6. Проверка контекста исключения

def process_data(data):
    if not data:
        raise ValueError("Данные пусты")
    return len(data)

def test_with_context():
    with pytest.raises(ValueError) as exc:
        process_data([])
    
    # Проверяем различные аспекты исключения
    assert exc.typename == ValueError
    assert "пусты" in str(exc.value)
    assert exc.traceback  # Traceback доступен

Лучшие практики

  • Проверяй специфичные исключения — не ловите Exception
  • Используй match для сообщений — проверяй содержание ошибки
  • Не забывай про negative tests — это критически важно для надежности
  • Параметризуй тесты — тестируй разные сценарии ошибок
  • Используй mock для внешних зависимостей — не делай реальные запросы

Хорошее покрытие негативных тестов — это залог стабильного приложения в production.