При подмене класса через unittest.mock.patch что получит код при создании экземпляра внутри тестируемой функции?
Код получит не настоящий экземпляр класса, а значение return_value мока, созданного для подменённого класса. По умолчанию это дочерний mock, поэтому вызовы методов такого «экземпляра» можно отдельно проверять и настраивать.
Подмена классов появилась как часть практики изоляции тестируемого кода от внешних зависимостей: файловой системы, сети, баз данных и сложных сервисов. Она позволяет проверить поведение функции, не создавая реальный объект и не вызывая его конструктор.
Такой подход особенно полезен, когда создание объекта само по себе имеет побочные эффекты или требует недоступной инфраструктуры. Цена изоляции — риск проверить взаимодействие с mock-объектом, а не совместимость с реальным классом.
Если разработчик ожидает, что patch вернёт настоящий экземпляр с подменёнными методами, тест может проверять неверную модель поведения. В действительности при вызове заменённого класса происходит вызов мока класса, а его результатом становится отдельный mock-экземпляр.
Важно также подменять именно то имя класса, которое использует тестируемый модуль. Иначе настоящий класс может остаться незатронутым, даже если одноимённый объект заменён в исходном модуле.
patch заменяет объект класса в области, где тестируемый код его ищет. Когда код выполняет создание экземпляра, фактически вызывается mock класса. Результат этого вызова находится в return_value мока класса.
Методы предполагаемого экземпляра нужно настраивать через return_value, а проверки вызова конструктора выполнять на самом mock класса. Например:
В примере user_class — mock класса, а user_class.return_value — mock созданного экземпляра. Вызов name регистрируется у экземпляра, тогда как вызов конструктора регистрируется у mock класса.
Если требуется дополнительно контролировать интерфейс, применяют autospec или явную спецификацию. Это снижает риск опечаток в именах методов и неправильных сигнатур, но не доказывает, что реализация корректно работает с настоящим классом. Для проверки интеграции с реальным конструктором и методами нужен отдельный тест без такой подмены.
Сервис создаёт клиент базы данных внутри метода. Вариант с настоящим клиентом реалистичен, но делает тест медленным, зависимым от окружения и нестабильным при недоступной базе. Вариант с подменой только метода клиента проще, однако всё ещё запускает конструктор и может выполнять нежелательные действия.
Подмена класса изолирует тест полностью: отдельно проверяется факт создания клиента, параметры конструктора и взаимодействие с экземпляром. Такой тест выбран для модульного слоя, а небольшой набор интеграционных тестов без patch сохраняет проверку совместимости с реальным клиентом.
Настройку делают через mock_класса.return_value, потому что именно этот объект возвращается при создании экземпляра. Настройка непосредственно mock_класса.method относится к mock класса и не заменяет метод его экземпляра.
assert_called_once_with у mock класса?Он проверяет единственный вызов самого заменённого класса и его аргументы конструктора. Он не проверяет, какие методы были вызваны у созданного mock-экземпляра; для этого проверяют объект return_value.
Да, если тестируемый код обращается к заменённому имени класса. Вызов настоящего конструктора не происходит, поскольку вместо класса вызывается mock. Если код сохранил ссылку на настоящий класс под другим именем или импортировал его в другую область, подмена выбранного имени может не повлиять на фактический вызов.