Тест с фикстурой monkeypatch завершается исключением: останутся ли внесённые ею изменения после завершения теста?
Нет. pytest откатит изменения, внесённые через фикстуру monkeypatch, во время teardown даже после исключения в тесте. Восстанавливаются изменённые атрибуты, элементы словарей, переменные окружения, текущий каталог и другие состояния, которыми управляет monkeypatch.
Автотестам часто требуется временно изменить глобальное состояние приложения: окружение, атрибут модуля, текущий каталог или путь импорта. Ручное изменение такого состояния без гарантированного отката приводит к зависимости тестов от порядка запуска.
Фикстура monkeypatch решает эту проблему, объединяя изменение состояния и его автоматическое восстановление в рамках жизненного цикла теста. Это типичный подход для тестовой инфраструктуры: временная подмена должна быть изолированной и безопасной даже при аварийном завершении проверки.
Если тест изменил переменную окружения или атрибут модуля и завершился исключением, код после места изменения может не выполниться. При ручном откате состояние останется изменённым, а следующие тесты начнут работать в другой среде.
Последствия могут быть неочевидными: тесты проходят по отдельности, но падают в общем запуске; результат зависит от порядка; параллельное выполнение получает трудно воспроизводимые ошибки.
Фикстура monkeypatch регистрирует каждое изменение. После завершения теста pytest выполняет teardown фикстуры, а monkeypatch восстанавливает прежние значения или удаляет объекты, которых раньше не существовало.
Откат выполняется даже если тело теста завершилось исключением. Для нескольких изменений сохраняется информация о старом состоянии; восстановление выполняется в обратном порядке, что важно при последовательных изменениях связанных объектов.
После завершения этого теста переменная APP_MODE вернётся к прежнему состоянию либо будет удалена, если до теста её не было. Само исключение при этом не отменяется: monkeypatch отвечает за очистку, а не за обработку ошибки.
Гарантия действует только для изменений, сделанных через данный экземпляр monkeypatch. Если тест напрямую присвоил значение атрибуту или изменил словарь без monkeypatch, pytest этого изменения не знает и автоматически его не отменит.
Стандартная фикстура monkeypatch имеет область действия function. Поэтому её нельзя напрямую запросить из фикстуры с более широкой областью, например module или session: это приведёт к конфликту областей действия. Для более узкого участка кода можно использовать отдельный контекст MonkeyPatch.context(), явно ограничив время действия подмены.
Тест проверяет обработку режима обслуживания и временно задаёт переменную окружения. Внутри теста возникает исключение до ручного восстановления значения.
Рассматривались три варианта. Прямое присваивание с восстановлением в конце проще, но не очищается при исключении. Конструкция try/finally надёжнее, однако при большом количестве изменений становится многословной и легко допускает ошибку в сохранении исходного состояния. unittest.mock.patch хорошо подходит для объектов и вызовов, но не так удобно покрывает переменные окружения, каталоги и системный путь.
Выбран monkeypatch, потому что он регистрирует изменения и связывает их очистку с teardown pytest. В результате тесты не зависели от порядка запуска, а исключения внутри тестов больше не оставляли загрязнённое окружение.
Что произойдёт, если изменить атрибут напрямую, а затем ожидать его автоматического восстановления?
Ответ: pytest не восстановит такое изменение. Автоматический откат выполняется только для операций, зарегистрированных конкретным экземпляром monkeypatch: например, через его методы изменения атрибутов, элементов словаря или переменных окружения.
Поэтому прямое присваивание нужно либо заменить на операцию monkeypatch, либо обернуть ручным try/finally. Смешивание двух подходов опасно: часть состояния будет очищаться автоматически, а часть останется на ответственности автора теста.
Можно ли использовать стандартную фикстуру monkeypatch внутри session-scoped фикстуры?
Ответ: напрямую нельзя. Стандартная фикстура monkeypatch имеет function scope, а фикстура с областью session живёт дольше. Запрос более узкой фикстуры из более широкой нарушает правила разрешения зависимостей pytest и приводит к ScopeMismatch.
Если изменение действительно должно жить дольше одного теста, обычно лучше явно управлять им в session-scoped фикстуре с помощью объекта MonkeyPatch и гарантированно вызвать его откат в teardown. Однако широкая область увеличивает риск влияния на другие тесты, поэтому предпочтительнее сохранять область подмены минимальной.
Что произойдёт с подменой, если тест запускает фоновый поток, который продолжает работу после его завершения?
Ответ: monkeypatch откатит состояние при teardown теста, даже если фоновый поток ещё выполняется. Поток после этого может увидеть уже восстановленное значение или обратиться к удалённому атрибуту, что создаёт гонку и нестабильность.
Поэтому фоновые задачи нужно явно остановить и дождаться их завершения до выхода из теста. Автоматический откат monkeypatch не синхронизирует потоки и не продлевает срок жизни подмены; он гарантирует только восстановление состояния в момент teardown.