Классическая ловушка с моками в Python: тест зелёный, но реальный API всё равно вызывается. Вот как это происходит и как проверить что мок реально перехватил вызов.
Симптом
Тест на исключение при невалидной валюте проходит. Но в моке настроен ответ
"result": "success" — который никогда не выбросит исключение. Как тест зелёный?Реальный API вернул ошибку для несуществующей валюты «CTM». Мок вообще не сработал.
Как доказать что мок перехватил вызов
with pytest.raises(CurrencyConversionError):
convert_currency(
amount=Decimal("1.00"),
from_currency="CAD",
to_currency="CTM",
)
mock_get.assert_called_once() # если упал — мок не перехватил, бил реальный API
assert_called_once() — не опциональная проверка, а обязательная часть теста когда вы мокаете внешние вызовы.Главное правило: патчить там где имя используется, не где определено
# модуль делает: import requests
# правильно:
mocker.patch("myapp.utils.currency.requests.get")
# если модуль делает: from requests import get
# то только так:
mocker.patch("myapp.utils.currency.get")
# requests.get здесь не перехватит ничего
Это самая частая причина почему мок «не работает».
Итоговый тест с pytest-mock
def test_bad_currency_raises(self, mocker):
mock_get = mocker.patch("myapp.utils.currency.requests.get")
mock_get.return_value.json.return_value = {
"result": "error",
"error-type": "unknown-code",
}
with pytest.raises(CurrencyConversionError):
convert_currency(Decimal("1.00"), "CAD", "CTM")
mock_get.assert_called_once()
Когда мок — не лучший инструмент
Если каждый тест завязан на конкретный import path — это тест реализации, не поведения. Альтернатива: адаптер-класс для внешнего вызова + fake-реализация + dependency injection. Тогда не нужно патчить вообще.
📍 Навигация: Вакансии • Задачи • Собесы
Библиотека питониста
#буст