Как в Python изолировать unit-тест от внешних сетевых вызовов

Ответ

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.