Обработка исключений в pytest

Обработка исключений в pytest на примерах

В процессе тестирования часто возникает необходимость проверить, что определенный код генерирует ожидаемое исключение. В pytest это можно сделать с помощью контекстного менеджера pytest.raises.

Проверка возникновения исключения

Допустим, вы хотите убедиться, что деление на ноль в Python вызывает ZeroDivisionError. Вы можете использовать pytest.raises для этого:

import pytest

def test_divide_by_zero():
    with pytest.raises(ZeroDivisionError):
        result = 1 / 0

Если в блоке with не возникнет исключение ZeroDivisionError, тест будет считаться не пройденным.

Проверка сообщения исключения

Иногда необходимо удостовериться, что исключение генерирует конкретное сообщение. Это можно сделать, передав аргумент match:

def test_divide_by_zero_message():
    with pytest.raises(ZeroDivisionError, match="division by zero"):
        result = 1 / 0

Если исключение возникло, но его сообщение не соответствует заданному регулярному выражению, тест тоже будет считаться не пройденным.

Использование исключения в качестве возвращаемого значения

Иногда после перехвата исключения вы можете захотеть провести дополнительные проверки. pytest.raises возвращает объект исключения, который можно дополнительно анализировать:

def test_divide_by_zero_analysis():
    with pytest.raises(ZeroDivisionError) as exc_info:
        result = 1 / 0
    
    assert "zero" in str(exc_info.value)

Отсутствие исключений

В некоторых ситуациях вы можете захотеть удостовериться, что исключение не возникает в блоке кода. В pytest нет прямого способа сделать это, но вам может помочь стандартная конструкция:

def test_no_exception():
    try:
        result = 1 / 1  # Этот код не должен вызывать исключение
    except Exception as e:
        pytest.fail(f"Unexpected exception raised: {e}")

В данном примере, если возникнет исключение, тест будет считаться не пройденным благодаря вызову pytest.fail().

Ожидание разных исключений

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

def test_multiple_exceptions():
    with pytest.raises((TypeError, ValueError)):
        result = int("not_a_number")

В этом примере тест пройдет, если возникнет либо TypeError, либо ValueError.

Использование параметризации с исключениями

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

import pytest

@pytest.mark.parametrize("input, exception_type", [
    ("not_a_number", ValueError),
    (None, TypeError)
])
def test_parametrized_exceptions(input, exception_type):
    with pytest.raises(exception_type):
        result = int(input)

Такой подход позволяет удобно тестировать различные сценарии ошибок в одном тестовом методе.

Устранение ложных срабатываний

При использовании pytest.raises важно помещать в блок with только ту часть кода, где действительно ожидается исключение. Это помогает избежать ложных срабатываний, когда исключение возникает не там, где ожидалось.

# Неправильно
with pytest.raises(ValueError):
    some_setup_function()
    result = int("not_a_number")

# Правильно
some_setup_function()
with pytest.raises(ValueError):
    result = int("not_a_number")

Заключение

Обработка исключений является ключевой частью многих тестовых сценариев, особенно когда вы хотите убедиться, что ваш код корректно реагирует на ошибки или аномальные ситуации. pytest предоставляет простой и интуитивно понятный механизм для тестирования и анализа исключений, что делает ваш код тестов более ясным и надежным.

Содержание: