Программирование PythonТестированиеPython-разработчик, отвечающий за автоматизированное тестирование

Вам нужно временно изменить окружение только на часть одного теста, чтобы после неё проверить исходное сост...

Вам нужно временно изменить окружение только на часть одного теста, чтобы после неё проверить исходное состояние. Какой механизм pytest monkeypatch обеспечивает ранний откат изменений?

Проходите собеседования с ИИ помощником Hintsage

Краткий ответ

Используйте monkeypatch.context(). Он создаёт отдельный контекст изменений и автоматически отменяет выполненные внутри него подмены при выходе из блока, даже если внутри возникло исключение.

Исторический контекст

Подмена окружения, атрибутов и элементов sys.path нужна тестам, чтобы изолировать код от внешних зависимостей. Проблема возникает, когда стандартного отката в конце теста недостаточно: одна часть теста должна работать с подменой, а другая — уже с восстановленным состоянием.

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

Постановка проблемы

Обычная фикстура monkeypatch отменяет свои изменения после завершения теста. Если внутри одного теста сначала нужно проверить поведение с изменённой переменной окружения, а затем — поведение без неё, одного автоматического teardown недостаточно.

Вызов monkeypatch.undo() может быть слишком грубым: он отменяет изменения, зарегистрированные в данном объекте monkeypatch, а не обязательно только одну логическую часть сценария. Ошибка в границах очистки приводит к проверке кода в неправильном окружении и к трудно диагностируемым результатам.

Подробное решение

monkeypatch.context() возвращает контекстный менеджер. Все изменения, выполненные через объект, выданный этим менеджером, регистрируются отдельно и отменяются при выходе из with.

import os def test_mode(monkeypatch): assert os.getenv("MODE") is None with monkeypatch.context() as patch: patch.setenv("MODE", "test") assert os.getenv("MODE") == "test" assert os.getenv("MODE") is None

Откат происходит и при нормальном выходе, и при исключении. Внутри можно использовать обычные операции setenv, delenv, setattr, delattr, setitem и другие методы monkeypatch.

Контекст создаёт отдельный объект управления изменениями. Поэтому для предсказуемого локального отката все подмены, относящиеся к этой области, следует выполнять через объект patch, а не смешивать их с изменениями внешней фикстуры monkeypatch.

monkeypatch.undo() полезен, когда нужно вручную отменить изменения всего текущего объекта monkeypatch. Для локальной границы внутри теста предпочтительнее monkeypatch.context(): он лучше выражает намерение и не затрагивает независимые подмены.

Ситуация из практики

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

Вариант с одной фикстурой monkeypatch прост, но подмена будет жить до конца теста. Вариант с ручным undo() даёт нужный результат, однако легко отменить больше изменений, чем планировалось, особенно если тест усложнится.

Выбран monkeypatch.context(), потому что он ограничивает подмену ровно вызовом загрузчика настроек. После выхода из контекста окружение автоматически возвращается к исходному состоянию, и вторая проверка не зависит от первой.

Что кандидаты часто упускают

  1. Отменяются ли изменения контекста, если тело блока завершилось исключением?

Да. Контекстный менеджер выполняет очистку в завершающей части своего протокола, поэтому исключение не предотвращает откат. Само исключение при этом не подавляется автоматически: оно продолжает распространяться, если тест или внешний обработчик его не перехватывает.

  1. Можно ли использовать несколько контекстов monkeypatch внутри одного теста?

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

  1. Что произойдёт, если один и тот же атрибут изменить внутри контекста несколько раз?

monkeypatch сохраняет исходное состояние и при откате восстанавливает его. Промежуточные значения действуют в соответствии с порядком подмен, но после завершения контекста атрибут возвращается к состоянию до первой подмены этого контекста.