Объясните механизм: что именно проверяет assert_called_once_with у unittest.mock.Mock и чего успешная проверка не доказывает?
assert_called_once_with проверяет, что данный объект Mock был вызван ровно один раз с указанными позиционными и именованными аргументами. Успешная проверка не доказывает, что была выполнена реальная реализация зависимости, корректно обработан её результат или достигнут нужный бизнес-эффект.
Моки появились как практический способ изолировать тестируемый компонент от внешних зависимостей: сети, базы данных, файловой системы и других сервисов. Вместо настоящей зависимости тест получает контролируемый объект, который можно настроить и затем проверить.
Проверка взаимодействия решает исходную проблему наблюдаемости: тест может подтвердить не только итоговое значение, но и соблюдение контракта вызова зависимости. Это особенно важно, когда сам результат недостаточен для проверки корректного поведения.
Тест может пройти с правильным результатом случайно: зависимость не вызвана, вызвана дважды или получила неверные параметры, но тестовая заглушка всё равно вернула ожидаемое значение. Без проверки взаимодействия такие ошибки остаются незамеченными.
Обратная опасность — проверять каждый внутренний вызов без необходимости. Тогда тест начинает зависеть от реализации, а не от наблюдаемого поведения, и становится хрупким при безопасном рефакторинге.
assert_called_once_with анализирует историю вызовов конкретного объекта Mock. Утверждение проходит только при одновременном выполнении двух условий: вызов был ровно один и его аргументы полностью совпали с переданными в проверку.
Здесь проверяется вызов дочернего мока client.get_user, а не реального метода клиента. Свойство return_value задаёт результат, который будет возвращён тестируемой функции, поэтому отдельная проверка result подтверждает обработку этого результата, но не достоверность работы настоящего клиента.
Проверка чувствительна к количеству вызовов: ноль вызовов и два вызова приводят к ошибке. Она также учитывает позиционные и именованные аргументы как разные формы вызова, поэтому изменение способа передачи аргумента может нарушить тест даже при эквивалентном для Python значении.
Для проверки только факта вызова без строгих аргументов используют assert_called, а для проверки последовательности или нескольких вызовов — историю вызовов и соответствующие утверждения. Однако ослабление проверки уменьшает её способность обнаруживать ошибки контракта.
Главное ограничение — мок подтверждает взаимодействие с подменой, а не поведение реальной зависимости. Поэтому контрактные или интеграционные тесты всё равно нужны там, где важно убедиться в совместимости с настоящим API.
Сервис оформления заказа должен один раз передать платёжному шлюзу идентификатор заказа и сумму. Варианты решения:
Выбран третий вариант для модульного теста: мок возвращает успешный ответ, а assert_called_once_with фиксирует ровно один вызов с нужными данными. Отдельный интеграционный тест проверяет совместимость с реальным или контрактным представлением шлюза, поэтому модульный тест не перегружается ответственностью интеграционной проверки.
1. Что произойдёт, если мок вызван дважды с одинаковыми аргументами?
assert_called_once_with завершится ошибкой, потому что совпадение аргументов не отменяет требования о ровно одном вызове. Для проверки двух ожидаемых вызовов нужно явно анализировать список вызовов, например через call_args_list или assert_has_calls, в зависимости от требуемой строгости порядка.
2. Проверяет ли assert_called_once_with возвращаемое значение мока?
Нет. Метод проверяет только количество вызовов и их аргументы. Возвращаемое значение задаётся отдельно через return_value или side_effect, а его обработку нужно проверять утверждением над результатом тестируемого кода либо отдельной проверкой поведения.
3. Чем опасна проверка вызова внутреннего помощника вместо внешнего контракта компонента?
Такая проверка связывает тест с конкретной структурой реализации. Если внутренний помощник заменить, объединить с другим или изменить порядок вызовов без изменения внешнего поведения, тесты начнут падать без пользовательской ошибки. Проверять вызов мока следует там, где он выражает значимый контракт с внешней зависимостью; внутренние детали лучше покрывать через наблюдаемое поведение.