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

При использовании нескольких декораторов patch как определить, в каком порядке их моки попадут в аргументы ...

При использовании нескольких декораторов patch как определить, в каком порядке их моки попадут в аргументы тестовой функции?

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

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

Моки от нескольких декораторов patch передаются в тестовую функцию снизу вверх: первым аргументом становится мок, созданный самым нижним декоратором. Поэтому порядок параметров функции нужно сопоставлять не с визуальным порядком декораторов сверху вниз, а с порядком их применения.

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

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

Когда подмен несколько, возникает дополнительная задача — передать созданные моки в тест для проверки вызовов и настройки поведения. Правило порядка аргументов связано с тем, как Python применяет вложенные декораторы.

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

Декораторы применяются снизу вверх, хотя записываются сверху вниз. Если перепутать это направление, тест может проверять вызовы не того мока или настраивать не ту зависимость.

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

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

Самый нижний декоратор применяется первым и оказывается ближе всего к исходной функции. При вызове обёртки он передаёт созданный мок первым позиционным аргументом, поэтому аргументы тестовой функции соответствуют декораторам снизу вверх.

from unittest.mock import patch @patch("service.Database") @patch("service.Clock") def test_report(mock_clock, mock_database): mock_clock.now.return_value = "today" build_report() mock_database.assert_called_once()

В примере нижний декоратор подменяет Clock, поэтому mock_clock — первый аргумент. Верхний декоратор подменяет Database, поэтому mock_database — второй.

Это правило относится к автоматически создаваемым мокам. Если для patch явно передать заменяющий объект через параметр new, новый мок для передачи в аргументы не создаётся, поэтому соответствующий параметр функции не добавляется. Фикстуры pytest при этом разрешаются отдельно: их имена не зависят от порядка декораторов и передаются как параметры, найденные pytest.

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

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

В тесте отчёта подменялись часы и база данных. Разработчик расположил параметры в визуальном порядке декораторов, из-за чего настройки часов применялись к моку базы данных. Ошибка не проявлялась сразу, поскольку оба мока допускали вызовы и возвращали настраиваемые значения.

Рассматривались два варианта. Можно было оставить декораторы и переставить параметры снизу вверх — это компактно и сохраняет автоматическое восстановление подмен. Другой вариант — использовать вложенные with patch(...), что нагляднее при сложной последовательности действий, но создаёт дополнительное вложение.

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

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

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

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

  2. Влияют ли фикстуры pytest на порядок аргументов, созданных patch?

    Да, но не в смысле изменения правила patch. Автоматически созданные моки передаются как позиционные аргументы обёртки, а фикстуры pytest разрешаются по именам параметров тестовой функции. Поэтому смешивание этих механизмов требует внимательно проверить сигнатуру: позиционные параметры от patch должны соответствовать нижнему порядку декораторов, а имена фикстур — существующим фикстурам pytest.

  3. Почему явная передача замены в patch меняет сигнатуру теста?

    Если patch получает готовый объект замены через new, ему не нужно создавать Mock для передачи в тестовую функцию. Следовательно, такой декоратор только подменяет объект, но не добавляет аргумент. Если ожидать дополнительный параметр, можно получить ошибку вызова функции или неверно сопоставить последующие аргументы.