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 к разработке поддерживаемых приложений. Вы разберётесь со структурой пакетов, объектной моделью, современной типизацией и автоматическими тестами, а затем соедините эти инструменты в небольшом приложении с хранением данных и логированием.
