В тесте подмена зависимости принимает аргументы, которых реальная функция не поддерживает. Как добиться, чтобы такая ошибка обнаруживалась во время теста?
Используйте спек-мок с автоспецификацией — autospec. Он создаёт замену на основе реального объекта и проверяет соответствие вызовов его интерфейсу, включая сигнатуру вызываемых функций и методов.
Без autospec обычный Mock обычно принимает произвольные аргументы, поэтому тест может пройти с неправильным вызовом и скрыть ошибку в production-коде.
Моки появились как средство изолировать тестируемый код от внешних зависимостей: сети, файловой системы, баз данных и других компонентов. Однако полностью свободная подмена создаёт новый риск — тест проверяет не настоящий контракт зависимости, а случайно придуманный интерфейс мока.
Автоспецификация решает эту проблему, связывая поведение мока с существующим объектом. Благодаря этому изоляция сохраняется, но расхождение между тестом и реальным API обнаруживается раньше.
Обычный мок может принять имя метода, которого нет у реального объекта, или аргументы в неправильном количестве. Такой тест формально успешен, хотя рабочий код может завершиться TypeError или обратиться к несуществующему атрибуту.
Особенно опасно это при изменении публичной функции: её сигнатура меняется, а тесты продолжают проходить, потому что созданный мок не знает о новом контракте. В результате тесты дают ложное чувство безопасности.
При создании мока включают autospec, передавая ему реальный объект или используя эквивалентный режим при patching. Мок анализирует исходный объект и ограничивает допустимые атрибуты и аргументы его интерфейсом.
В примере мок сохраняет вызываемость send_message, но вызов без обязательного аргумента приводит к TypeError. Это проверяет не результат настоящей отправки, а корректность взаимодействия с зависимостью.
Для объектов и классов автоспецификация также ограничивает доступ к атрибутам, которых нет в исходном объекте. При работе с классом важно учитывать разницу между спецификацией класса и экземпляра: методы класса имеют одну форму вызова, а методы экземпляра — другую после связывания с экземпляром.
autospec не проверяет внутреннюю бизнес-логику зависимости и не гарантирует, что переданные значения семантически корректны. Он контролирует форму интерфейса, а не смысл данных. Поэтому проверки результата, состояния и важных аргументов всё равно должны оставаться в тесте.
Главный компромисс — более тесная связь теста с интерфейсом реальной зависимости. Это полезно для защиты контракта, но при намеренно гибком или динамическом API может потребовать дополнительной настройки либо сделать тесты чувствительными к допустимым изменениям интерфейса.
Команда тестировала сервис, который отправляет уведомления. Тест заменял клиент обычным Mock, а рабочий код после рефакторинга начал передавать в метод клиента дополнительный параметр. Тест продолжил проходить, хотя реальный клиент этот параметр не принимал.
Рассматривались два варианта. Можно было вручную проверять список вызванных аргументов — это точно для конкретного теста, но требует обновлять проверки при каждом изменении интерфейса. Можно было заменить мок интеграционным тестом с настоящим клиентом — это лучше проверяет совместимость, но медленнее, сложнее и зависит от внешней инфраструктуры.
Выбрали autospec для модульных тестов и отдельный интеграционный тест для проверки совместимости с реальным клиентом. В результате ошибочные вызовы обнаруживались сразу на уровне модульного теста, а интеграционный тест оставался границей проверки фактического взаимодействия.
Что именно проверяет autospec: только наличие атрибутов или ещё и сигнатуры?
Он проверяет оба аспекта в пределах доступной информации о реальном объекте. Мок ограничивает неизвестные атрибуты и контролирует допустимые параметры вызова функций и методов. При этом он не валидирует типы аргументов и не определяет, является ли переданное значение бизнес-смысловым.
Почему автоспецификация не заменяет проверку вызовов мока?
autospec отвечает на вопрос «можно ли так вызвать зависимость», но не на вопрос «нужно ли было вызвать её именно так». Код может корректно соблюдать сигнатуру, но передавать неверного получателя, неправильный текст или вызывать зависимость лишний раз. Поэтому отдельно применяют проверки вроде количества вызовов и значений аргументов.
Какая проблема возникает при автоспецификации объектов с динамическими атрибутами?
Если атрибут создаётся во время выполнения и отсутствует в классе или экземпляре на момент построения спецификации, автоспецификация может не распознать его как допустимый. Это снижает удобство для динамических объектов и иногда требует использовать явную спецификацию, адаптер с заранее объявленным интерфейсом или более подходящую границу мокирования. Ослаблять проверку следует только осознанно, иначе теряется защита от рассинхронизации API.