Вопрос с собеседования · Python

Как работает unittest.mock.patch и почему патчить нужно там, где объект используется?

MiddleТестирование
Ещё спрашивают так:
  • Почему 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 без изменений

Разбор темы

Связанные вопросы

Партнёр платформы

Разбор собеседования с sotto

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

Разбор собеседования