Ответ
Unit-тесты по определению должны быть быстрыми, детерминированными и изолированными. Вызов реальных внешних ресурсов (API, базы данных) нарушает эти принципы. Для изоляции тестов от сетевых вызовов используется техника мокинг (mocking).
Основной инструмент в стандартной библиотеке Python — unittest.mock. Он позволяет заменить реальные объекты (функции, классы) на "заглушки" (моки) во время выполнения теста.
Пример с unittest.mock.patch:
Предположим, есть функция, которая обращается к внешнему API:
# my_module.py
import requests
def get_user_data(user_id):
response = requests.get(f'https://api.example.com/users/{user_id}')
response.raise_for_status() # Вызовет исключение для плохих статусов
return response.json()
Тест для этой функции будет выглядеть так:
# test_my_module.py
from unittest.mock import patch
from my_module import get_user_data
# @patch заменяет 'requests.get' на мок-объект
@patch('my_module.requests.get')
def test_get_user_data(mock_get):
# 1. Настраиваем мок: что он должен вернуть при вызове
mock_response_data = {'id': 1, 'name': 'John Doe'}
mock_get.return_value.status_code = 200
mock_get.return_value.json.return_value = mock_response_data
# 2. Вызываем тестируемую функцию
user_data = get_user_data(1)
# 3. Проверяем результат и то, что мок был вызван правильно
mock_get.assert_called_once_with('https://api.example.com/users/1')
assert user_data == mock_response_data
Преимущества этого подхода:
- Скорость: Тест выполняется мгновенно, без реальной сетевой задержки.
- Надежность: Результат теста не зависит от доступности API или состояния сети.
- Контроль: Можно легко эмулировать любые сценарии, включая ошибки (например,
status_code = 404), для проверки обработки исключений.
Для более удобной работы с HTTP-запросами также популярна библиотека responses.
Ответ 18+ 🔞
А, ну вот, смотри, классическая история, блядь! Ты пишешь код, который лезет куда-то в интернеты, в базы, а потом пытаешься это протестить. И тут тебе пиздец как весело: то сеть легла, то API сдохло, то в базе данных хуйня какая-то. Тесты падают, а ты нихуя не понимаешь — это твой код говно или просто мир вокруг ебнулся?
Так вот, чтобы не сойти с ума, умные дядьки придумали мокинг, ёпта. Суть проста, как три копейки: ты на время теста подменяешь реальную, ебанутую, зависимую от всего на свете хрень на свою управляемую заглушку. И тестируешь уже не «дошло ли письмо до Китая», а «правильно ли твоя функция пытается это письмо отправить».
В Питоне за это отвечает unittest.mock. Главный герой там — декоратор patch. Он как хирург: аккуратно вырезает кусок кода и вставляет туда свою железяку на время операции.
Смотри, вот у тебя есть функция, которая лезет за данными в какую-то левую апишку:
# my_module.py
import requests
def get_user_data(user_id):
response = requests.get(f'https://api.example.com/users/{user_id}')
response.raise_for_status() # Вызовет исключение для плохих статусов
return response.json()
Если тестить её в лоб, то это будет ебаный цирк с конями. А вот как это делают по-человечески:
# test_my_module.py
from unittest.mock import patch
from my_module import get_user_data
# Смотри, какая магия, блядь! Говорим: «В этом тесте везде, где в my_module встречается requests.get, подставь нашу подделку»
@patch('my_module.requests.get')
def test_get_user_data(mock_get):
# 1. Настраиваем нашу поддельную «ответку». Словно кукловод, блядь!
mock_response_data = {'id': 1, 'name': 'John Doe'}
mock_get.return_value.status_code = 200
mock_get.return_value.json.return_value = mock_response_data
# 2. Вызываем нашу функцию. Она даже не подозревает, что её наёбывают!
user_data = get_user_data(1)
# 3. А теперь проверяем, всё ли прошло по нашему сценарию.
# Функция вызвала requests.get с правильным URL? Проверим!
mock_get.assert_called_once_with('https://api.example.com/users/1')
# И вернула ли она то, что мы ей подсунули?
assert user_data == mock_response_data
И в чём, собственно, кайф, ёпта?
- Скорость ебать! Тест отрабатывает за миллисекунды. Никаких ожиданий ответа от какого-то там сервера, который может в этот момент сосать лапу.
- Стабильность, мать её! Результат теста зависит только от твоего кода, а не от фазы луны или настроения админа удалённого сервака.
- Полный контроль, как в танке! Хочешь проверить, как твой код отреагирует на ошибку 404? Пожалуйста:
mock_get.return_value.status_code = 404. На таймаут? Легко! Эмулируй любую пиздецовую ситуацию, не выходя из кабинета.
Есть, конечно, и другие библиотеки, вроде responses, которые для HTTP-запросов ещё удобнее могут быть. Но unittest.mock — это как швейцарский нож, всегда под рукой в стандартной библиотеке. Освой его, и половина твоих проблем с тестированием просто испарится, в рот меня чих-пых!