pytest.warns: как проверить устаревший интерфейс перед удалением

Библиотека переименовала аргумент, но старые вызовы ещё должны работать один выпуск. Старый интерфейс обязан вернуть прежний результат и предупредить пользователя. Проверка только числа оставит незамеченным исчезнувшее предупреждение.

Предупреждение — отдельная часть поведения

warnings.warn сообщает о нежелательном, но пока допустимом использовании. Для устаревающего API обычно используют DeprecationWarning. Фильтры Python могут скрывать его при обычном запуске, поэтому смотреть на консоль вручную недостаточно. pytest.warns перехватывает предупреждение указанного класса и проверяет, что оно действительно появилось.

Параметр stacklevel помогает указать пользователю место его вызова, а не строку внутри библиотеки. Для простой обёртки подходит 2. В длинной цепочке обёрток уровень выбирают по фактическому стеку. Проверка местоположения нужна, если библиотека обещает удобную диагностику миграции.

Проверить старый и новый путь

Пример сохраняет старую функцию double_old как обёртку над новой. Тест проверяет вычисленный результат и текст предупреждения. Новый интерфейс тестируется отдельно: он не должен предупреждать при нормальном использовании. Иначе пользователь получит шум даже после правильного перехода.

Сохраните код в test_deprecation.py и выполните pytest. catch_warnings во втором тесте явно включает все предупреждения на время проверки. Это позволяет отличить их отсутствие от ситуации, когда фильтр просто скрыл сообщение.

import warnings
import pytest

def double(value):
    return value * 2

def double_old(value):
    warnings.warn("use double instead", DeprecationWarning, stacklevel=2)
    return double(value)

def test_old_api_warns_and_keeps_result():
    with pytest.warns(DeprecationWarning, match="use double instead") as captured:
        result = double_old(7)
    assert result == 14
    assert len(captured) == 1
    assert captured[0].filename == __file__

def test_new_api_is_quiet():
    with warnings.catch_warnings(record=True) as captured:
        warnings.simplefilter("always")
        assert double(7) == 14
    assert captured == []

Не подавлять предупреждения всем проектом

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

Перед удалением старой функции полезно сохранить тест её текущего контракта, перевести потребителей и затем изменить ожидаемое поведение вместе с выпуском. Предупреждение помогает найти вызовы, но само по себе не доказывает, что все пользователи уже перешли.

Добавить второй наблюдаемый результат

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

Продолжить на практике

  • Python для продвинутых: ООП, типизация и тестирование ↗

    Практический курс-мост от базового Python к разработке поддерживаемых приложений. Вы разберётесь со структурой пакетов, объектной моделью, современной типизацией и автоматическими тестами, а затем соедините эти инструменты в небольшом приложении с хранением данных и логированием.