Как работает unittest.mock.patch и почему патчить нужно там, где объект используется?
- Почему mock.patch не подменил функцию?
- Когда моки вредят тестам?
patch временно заменяет атрибут по имени на Mock. Подменять нужно в модуле, где имя ищется, а не там, где оно определено: если orders.py сделал from payments import charge, патчить orders.charge, а не payments.charge.
- autospec=True — мок с сигнатурой оригинала: неверный вызов падает
- return_value, side_effect (исключение или последовательность), assert_called_once_with
- Для async — AsyncMock
А ещё Много моков — признак сильной связанности; лучше фейковые реализации и интеграционные тесты
Подробно
Почему «там, где используется». from payments import charge создаёт в модуле orders своё имя charge, указывающее на функцию. Патч payments.charge меняет имя в модуле payments, а orders по-прежнему держит старую ссылку. Если же в orders написано import payments и вызывается payments.charge(...) — патчить payments.charge.
autospec. Обычный Mock принимает любые аргументы и любые атрибуты: опечатка fake.assert_called_once_wiht() раньше молча проходила. С autospec=True мок повторяет сигнатуру и набор атрибутов оригинала.
Когда моки вредят. Тест, проверяющий, какие методы вызваны в каком порядке, ломается при любом рефакторинге и не ловит ошибки интеграции. Мокают границы системы (HTTP, время, случайность), а внутренние классы — реальными объектами или простыми фейками.
Пример
# orders.py
from payments import charge
def checkout(cart): return charge(cart.total)
# test_orders.py
from unittest.mock import patch
def test_checkout():
with patch("orders.charge", autospec=True) as fake:
fake.return_value = "ok"
assert checkout(Cart(total=100)) == "ok"
fake.assert_called_once_with(100)Что хотят услышать
- Правило «где используется» с примером.
- autospec и основные возможности Mock.
- Риски избыточного мокирования.
Проверьте себя
Один верный вариант. Подумайте, потом откройте ответ.
Показать варианты и ответ
- ✗При from payments import charge в orders.py патчат payments.charge — там, где функция определена
- ✗patch навсегда заменяет функцию во всём процессе
- ✓При from payments import charge в orders.py патчат orders.charge — там, где имя ищется
- ✗Для асинхронных функций используют обычный Mock без изменений
Разбор темы
- Окружение, импорты и тесты на pytest
Виртуальные окружения и зависимости, как работает импорт и чем опасны циклические импорты, __name__ == "__main__", pytest, фикстуры и mock.patch.
Связанные вопросы
Разбор собеседования с sotto
После собеседования sotto разбирает разговор: оценка, каждый вопрос — хорошо, частично или слабо, чего не хватило в ответе и над чем поработать к следующему разу. Первые 30 минут бесплатно.