В асинхронном тесте зависимостью является функция, которую код должен вызвать через await. Какой тип mock корректно подменит такую зависимость?
Используйте AsyncMock. Он возвращает awaitable-объект, поэтому результат его вызова можно передать в await, а сам факт ожидания проверить отдельно от факта вызова.
Обычный Mock по умолчанию возвращает произвольное значение, которое не обязано быть awaitable. Поэтому код с await обычно завершится ошибкой типов во время выполнения теста.
Обычные mock-объекты изначально предназначались для синхронного кода: вызов зависимости фиксировался, а её результат задавался обычным значением. С появлением асинхронных функций этого стало недостаточно, поскольку асинхронный вызов имеет дополнительный этап — ожидание результата.
AsyncMock решает именно эту проблему: он моделирует не только вызов функции, но и поведение возвращаемого awaitable-объекта.
Предположим, production-код вызывает асинхронный клиент и ожидает его результат. Если подменить клиент обычным Mock, тест может упасть на await, хотя сама проверяемая логика корректна.
Обратная ошибка тоже опасна: если использовать AsyncMock для синхронной функции, код может получить coroutine вместо обычного результата. Такой тест либо сразу завершится ошибкой, либо скроет неверный контракт зависимости.
Важно различать два события: mock могли вызвать, но возвращённый awaitable могли не ожидать. Для асинхронного контракта обычно требуется проверять оба условия.
AsyncMock следует применять, когда подменяемая зависимость объявлена как асинхронная функция или по контракту возвращает объект, который должен быть обработан через await.
Вызов AsyncMock регистрируется как вызов, а ожидание результата — как await. Поэтому assert_called_once_with проверяет только факт вызова и его аргументы, тогда как assert_awaited_once_with подтверждает, что возвращённый awaitable действительно был ожидаем ровно один раз с указанными аргументами.
Если асинхронная зависимость вызывается, но её результат намеренно не ожидается, проверка должна отражать именно такой контракт. Однако для обычного запроса к асинхронному клиенту отсутствие await чаще означает ошибку реализации, которую тест должен обнаружить.
При создании mock полезно сохранять исходный интерфейс зависимости через spec или autospec, если это соответствует используемому способу подмены. Это помогает обнаруживать неправильные имена и сигнатуры, но не заменяет проверку того, что вызов был именно ожидаем.
Сервис отправляет асинхронный запрос в клиент уведомлений и возвращает идентификатор сообщения. В первом варианте разработчик использовал обычный Mock. Тест падал на await, поскольку mock возвращал строку вместо awaitable-объекта.
Второй вариант использовал AsyncMock, но проверял только assert_called_once_with. Такой тест подтверждал, что метод вызвали, но не гарантировал, что код дождался результата. При забытом await это могло привести к ложноположительной проверке вызова и предупреждению о неожидаемой coroutine.
Выбран AsyncMock с проверкой результата и assert_awaited_once_with. Это одновременно проверяет корректный тип подмены, аргументы и выполнение обязательного этапа ожидания.
assert_called_once_with для AsyncMock?Нет. Эта проверка подтверждает только регистрацию вызова mock и совпадение аргументов. Она не доказывает, что возвращённый awaitable был передан в await; для этого нужна проверка assert_awaited_once_with или соответствующая проверка количества ожиданий.
Вызов вернёт awaitable-объект, а не обычный результат. Если production-код ожидает строку, число или другой синхронный объект, он получит объект coroutine-подобного поведения и может сломаться позже, например при сравнении или сериализации. Тип mock должен соответствовать контракту заменяемой зависимости, а не удобству теста.
Проверка assert_awaited_once_with подтверждает, что await произошёл, но сама по себе не доказывает, что возвращённое значение повлияло на результат тестируемой функции. Если результат игнорируется, тест может не заметить ошибку передачи данных дальше. Поэтому нужно отдельно проверять итоговое поведение системы, а проверки mock использовать для значимых взаимодействий с зависимостью.