Как мокать зависимости в тестах?
Правильная организация подмены зависимостей в тестах позволяет изолировать тестируемый код от внешнего мира, такого как базы данных, сетевые запросы или файловая система. В экосистеме Python стандартным инструментом для решения этой задачи является встроенная библиотека unittest.mock, которая предоставляет такие мощные сущности, как patch и MagicMock. С их помощью можно легко заменять реальные объекты заглушками, отслеживать факты их вызовов и даже подменять возвращаемые значения для имитации различных сценариев работы приложения.
При использовании декоратора или контекстного менеджера patch критически важно правильно выбирать место для подмены. Мокать всегда следует именно там, где зависимость используется в тестируемом коде, а не там, где она изначально определена. Например, если функция из модуля а импортирует клиент из модуля б, то патчить нужно имя клиента в пространстве имен модуля а, а не в исходном файле модуля б. Нарушение этого правила часто приводит к тому, что тесты продолжают обращаться к реальным ресурсам, вызывая падения и непредсказуемое поведение.
Помимо явного патчинга, хорошей практикой проектирования является внедрение зависимостей через параметры функций и классов, известное как dependency injection. Такой подход изначально избавляет разработчика от необходимости использовать сложные инструменты перехвата, так как в тесты можно просто передать готовый мок-объект. После выполнения проверяемого кода обязательно используйте специализированные утверждения, например assert_called_once_with, чтобы убедиться, что функция была вызвана ровно один раз с ожидаемыми аргументами.
При этом важно соблюдать баланс и не превращать каждый тест в точную копию реализации. Тест должен проверять контракт и результат работы функции, а не ее внутреннее устройство, иначе любое незначительное изменение кода потребует полной переписки всех тестов.