Ответ
Мокирование (подмена) запросов — это ключевая техника для создания изолированных, быстрых и стабильных тестов. Основные причины её использования:
- Изоляция тестируемого модуля: Позволяет тестировать логику клиентского кода или сервиса, не завися от работы реального внешнего API, базы данных или другого микросервиса. Если тот «упал» или изменился, мои тесты всё равно будут проходить.
- Тестирование сложных и редких сценариев: Я могу легко смоделировать ответы, которые сложно или невозможно получить от реального сервиса в тестовом окружении.
- Ошибки сервера: 500 Internal Server Error, 503 Service Unavailable.
- Нестандартные ответы: Очень большие данные, специфичные форматы, медленные ответы (таймауты).
- Edge-кейсы: Ответ с пустым телом, невалидный JSON.
- Ускорение выполнения тестов: Моки работают в памяти, что в сотни раз быстрее, чем реальные сетевые вызовы.
- Предотвращение side-эффектов: Тест не создаст запись в реальной продакшн-БД или не отправит настоящее SMS-сообщение.
Практический пример (Python, pytest с pytest-mock):
Допустим, у меня есть функция get_user_data(user_id), которая внутри делает HTTP-запрос. Я хочу протестировать её логику обработки ответа.
import pytest
import requests
# Функция, которую тестируем
def get_user_data(user_id):
response = requests.get(f"https://api.example.com/users/{user_id}")
if response.status_code == 200:
return response.json()["data"]
elif response.status_code == 404:
return None
else:
raise Exception("API Error")
# Сам тест с моком
def test_get_user_data_success(mocker): # 'mocker' — фикстура из pytest-mock
# 1. Создаём мок-ответ, который имитирует успешный API-ответ
mock_response = mocker.Mock()
mock_response.status_code = 200
mock_response.json.return_value = {"data": {"id": 123, "name": "Alice"}}
# 2. Подменяем реальный `requests.get` на наш мок
# Теперь при вызове requests.get() в функции вернётся mock_response
mocker.patch("requests.get", return_value=mock_response)
# 3. Вызываем тестируемую функцию
result = get_user_data(123)
# 4. Проверяем, что функция корректно обработала мок-ответ
assert result == {"id": 123, "name": "Alice"}
# Дополнительно можно проверить, что запрос был вызван с правильным URL
# requests.get.assert_called_once_with("https://api.example.com/users/123")
def test_get_user_data_not_found(mocker):
mock_response = mocker.Mock()
mock_response.status_code = 404
mocker.patch("requests.get", return_value=mock_response)
result = get_user_data(999)
assert result is None # Проверяем обработку 404 ошибки
Важно помнить, что моки должны максимально точно имитировать поведение реального сервиса, и их стоит использовать для unit- и component-тестов. Для интеграционного и end-to-end тестирования предпочтительнее использовать реальные сервисы или их точные стабы (stubs).
Ответ 18+ 🔞
Слушай, ну это ж классика, ебать мои старые костыли! Мокирование — это когда ты такой: «А давайте-ка я сейчас этот внешний сервис, который вечно тормозит и падает, на свою подставную куклу заменю, чтобы не ебться с ним». Суть в том, чтобы твои тесты были изолированными, быстрыми и стабильными. А то без этого — терпения ноль, ебть.
Зачем это вообще нужно, спросишь? Ну, во-первых, изоляция. Ты тестируешь свою логику, а не то, жив ли сегодня какой-то левый API на другом конце света. Упал он — а твоим тестам похуй, они проходят, потому что работают с твоей подделкой. Красота же!
Во-вторых, *тестирование всякого пиздца**, который в реальной жизни получить — тот ещё геморрой. Хочешь проверить, как твой код отреагирует на ошибку 500? Или на ответ, который медленнее смерти черепахи? Или на такой кривой JSON, что хоть святых выноси? С моками — раз плюнуть. Задал нужный ответ и поехал дальше.
В-третьих, скорость. Моки в памяти работают, это в сотни раз быстрее, чем тащиться по сети до реального сервиса. Тесты за секунды пролетают, а не за минуты.
Ну и наконец, безопасность. Ты же не хочешь, чтобы твой тест случайно наделал записей в продакшен-базу или разослал всем клиентам тестовые смски? Вот и я о том же. Моки не оставляют следов.
Пример на пальцах (Python, pytest):
Допустим, есть у тебя функция, которая тащит данные пользователя по API. А я хочу проверить, как она обрабатывает успешный ответ.
import pytest
import requests
# Функция, которую тестируем
def get_user_data(user_id):
response = requests.get(f"https://api.example.com/users/{user_id}")
if response.status_code == 200:
return response.json()["data"]
elif response.status_code == 404:
return None
else:
raise Exception("API Error")
# Сам тест с моком
def test_get_user_data_success(mocker): # 'mocker' — фикстура из pytest-mock
# 1. Лепим муляж ответа от API
mock_response = mocker.Mock()
mock_response.status_code = 200
mock_response.json.return_value = {"data": {"id": 123, "name": "Alice"}}
# 2. Подменяем реальный `requests.get` на нашу подставу
# Теперь когда в функции вызовут requests.get(), получится наш mock_response
mocker.patch("requests.get", return_value=mock_response)
# 3. Дёргаем нашу функцию
result = get_user_data(123)
# 4. Проверяем, что она не обделалась и правильно всё распарсила
assert result == {"id": 123, "name": "Alice"}
# Можно ещё проверить, что запрос был именно с тем URL, который нужен
# requests.get.assert_called_once_with("https://api.example.com/users/123")
def test_get_user_data_not_found(mocker):
mock_response = mocker.Mock()
mock_response.status_code = 404
mocker.patch("requests.get", return_value=mock_response)
result = get_user_data(999)
assert result is None # Проверяем, что на 404 она возвращает None
Главное помнить — моки должны вести себя как близнецы реальных сервисов, а не как мартышк* с гранатой. И да, это оружие для юнит- и компонентных тестов. Для интеграционных проверок лучше уже что-то ближе к реальности использовать.