Откуда create_autospec берёт сигнатуру метода экземпляра и почему при вызове такого mock-метода не передают self вручную?
create_autospec извлекает сигнатуру метода из реального класса или объекта с помощью интроспекции Python. Если mock представляет метод экземпляра, он имитирует уже связанный метод, поэтому self передавать вручную не нужно: в проверяемую сигнатуру входят только пользовательские аргументы.
Обычный Mock по умолчанию допускает почти любые атрибуты и аргументы вызова. Это удобно для быстрой изоляции зависимостей, но позволяет тесту успешно пройти даже после изменения сигнатуры реального метода.
spec появился как ограничение интерфейса, а autospec развивает этот подход: он не только ограничивает доступные атрибуты, но и воспроизводит сигнатуры вызываемых объектов. Так тесты раньше обнаруживают несовместимость кода с реальным API.
Рассмотрим сервис с методом экземпляра. Если заменить его обычным mock, тест может вызвать метод с неправильным количеством или именами аргументов, не заметив ошибку.
При использовании create_autospec важно понимать границу между методом класса и связанным методом экземпляра. Ошибочное ручное добавление self приводит к ложной ошибке о лишнем аргументе и маскирует правильное поведение тестируемого кода.
При создании autospec для класса Python анализирует его атрибуты и методы. Для метода экземпляра исходная функция формально содержит self, но при обращении через экземпляр Python связывает объект с этой функцией и делает self внутренним первым аргументом.
create_autospec воспроизводит именно этот пользовательский интерфейс. Поэтому mock экземпляра принимает аргументы так, как их принимает обычный объект: без self.
Вызов service.send("готово", urgent=True) корректен, а позиционная передача второго аргумента нарушает исходную сигнатуру. Такая проверка выполняется самим mock до фактической записи вызова.
Autospec проверяет форму вызова: количество аргументов, их имена и возможность передачи позиционно или по имени. Он не проверяет типы значений, бизнес-смысл аргументов и результат работы реальной реализации.
Если autospec создаётся для класса без instance=True, результат представляет mock класса. Его return_value обычно используется как mock создаваемого экземпляра; методы этого результата также имеют сигнатуры экземпляра. Для уже существующего объекта можно передать сам объект в create_autospec.
Механизм зависит от доступности информации во время интроспекции. Атрибуты, добавляемые динамически только в __init__ или во время выполнения, могут отсутствовать в спецификации, если их нельзя обнаружить по классу или переданному объекту. Поэтому autospec повышает точность теста, но иногда требует явного описания интерфейса или выбора реального экземпляра для спецификации.
В проекте клиент внешнего API изменил метод отправки: обязательный параметр стал именованным, а один старый параметр был удалён. Тесты с обычным Mock продолжили проходить, потому что mock принимал прежний вызов без проверки сигнатуры. Ошибка проявилась только при обращении к реальному API.
Можно было оставить обычный Mock: это минимальная настройка и удобный вариант для полностью динамических протоколов, но он не защищает от несовместимых вызовов. Можно было вручную проверять assert_called_once_with, однако это проверяет конкретный сценарий и не всегда обнаруживает ошибку в форме вызова до записи вызова.
Выбран был create_autospec для клиента внешнего API. Он сохранил изоляцию от сети и одновременно начал немедленно выявлять неправильные имена, количество и способ передачи аргументов. В результате изменение контракта стало заметно на этапе запуска unit-тестов, хотя проверка типов и реального ответа API по-прежнему осталась задачей интеграционных тестов.
Да. Autospec проверяет вызов, а не аннотации типов и не содержимое аргументов. Если метод ожидает строку, передача числа может быть принята, если количество и способ передачи аргументов соответствуют сигнатуре. Для проверки типов нужны отдельные средства, например статический анализ, а для бизнес-правил — явные проверки в тесте.
Нет. Autospec ограничивает интерфейс mock и его вызовы, но не исполняет реальную реализацию и не выводит автоматически тип результата. Возвращаемое значение нужно настроить явно, а его корректность проверяется логикой теста или отдельным контрактным тестом.
В этом случае autospec может получить спецификацию самого mock, а не исходного реального объекта. Тогда ожидаемая защита сигнатуры ослабевает или отражает уже искажённый интерфейс. Надёжнее создавать autospec от настоящего класса или объекта до подмены, либо передавать в patch реальный объект как цель автоспецификации.