Мокирование запросов с помощью Python: использование mock package

Пройдите тест, узнайте какой профессии подходите

Я предпочитаю
0%
Работать самостоятельно и не зависеть от других
Работать в команде и рассчитывать на помощь коллег
Организовывать и контролировать процесс работы

Быстрый ответ

Сначала можно воспользоваться библиотекой requests-mock. Она позволяет легко формировать заранее определенные ответы для тестов:

Python
Скопировать код
import requests
from requests_mock import Adapter

session = requests.Session()
adapter = Adapter()
session.mount('http://', adapter)

# Метод мокирования в действии.
adapter.register_uri('GET', 'http://example.com', text='имитированный контент')
response = session.get('http://example.com')

# И вот, мы получаем 'имитированный контент'!
assert response.text == 'имитированный контент'

В данном примере при помощи requests_mock.Adapter мы настраиваем объект requests.Session возвращать на GET-запрос к 'http://example.com' заранее заготовленное содержимое. Таким образом, мы ускоряем работу тестов и делаем их надёжными, всё это без обращения к реальному серверу.

Кинга Идем в IT: пошаговый план для смены профессии

Альтернативы для управления запросами

Если вам необходимы более детализированные настройки, обратите внимание на responses. Этот инструмент предлагает удобный способ замены ответов в вашем приложении:

Python
Скопировать код
import responses
import requests

@responses.activate
def test_simple():
    # Ответ уже в очереди!
    responses.add(responses.GET, 'http://example.com', json={'key': 'value'}, status=200)
    resp = requests.get('http://example.com')

    # Проверка результата
    assert resp.json() == {'key': 'value'}
    assert len(responses.calls) == 1
    assert responses.calls[0].request.url == 'http://example.com'
    assert responses.calls[0].response.text == '{"key": "value"}'

Эпоха асинхронности: httpx и respx

Для асинхронных запросов можно применить сочетание библиотек httpx и respx.

Python
Скопировать код
import httpx
import respx
from httpx import Response

@respx.mock
async def test_httpx_async():
    # Асинхронный тест выполнен мгновенно!
    respx.get("https://test.org/").mock(return_value=Response(200, content=b'Test'))
    async with httpx.AsyncClient() as client:
        response = await client.get("https://test.org/")
        # Получаем ровно то, что и ожидали!
        assert response.content == b'Test'

Визуализация

Давайте представим процесс мокирования как театральную постановку:

Markdown
Скопировать код
Сцена (💻): Ваше Приложение
Актер (🎭): requests
Роль (📜): GET 'https://api.example.com/data'

Для этого подключим дублёра (👥 мок):

Python
Скопировать код
from unittest.mock import MagicMock
requests.get = MagicMock(return_value='Имитированный Контент')

Вот как проходит взаимодействие:

Markdown
Скопировать код
💻 --> 🎭: "Сделай GET-запрос!"
👥 (Мок): "Получите вот этот 'Имитированный Контент'!"

Суммируя сценарий:

  • Приложение (💻) уверено, что общается с настоящим «requests»,
  • Но дублёр (👥) играет свою роль и предоставляет имитированное исполнение.

Создание ваших имитаций

Рассмотрим более сложные сценарии с примерами создания имитаций:

Тестирование нескольких URL

Для тестирования нескольких URL с разными HTTP-методами отлично подойдет HTTPretty.

Python
Скопировать код
import httpretty
import requests

@httpretty.activate
def test_requests():
    # Два URL, два ответа – всё в одном месте!
    httpretty.register_uri(httpretty.GET, 'http://example1.com', 'Ответ 1')
    httpretty.register_uri(httpretty.POST, 'http://example2.com', 'Ответ 2')

    response1 = requests.get('http://example1.com')
    response2 = requests.post('http://example2.com')

    # Проверка результата
    assert response1.text == 'Ответ 1'
    assert response2.text == 'Ответ 2'

Модульное тестирование с имитированием

unittest.mock.patch позволяет имитировать вызовы в модульных тестах:

Python
Скопировать код
from unittest.mock import patch
from my_module import my_function

def test_my_function():
    with patch('my_module.requests.get') as mock_get:
        # Имитация обеспечивает мягкую работу теста
        mock_get.return_value.json.return_value = {'data': 'test'}
        result = my_function()
        assert result == 'test'

Имитирование Django-представлений

Отделите тесты от основного кода для лучшей читабельности и поддержки. Вот пример тестирования Django view:

Python
Скопировать код
from django.test import TestCase
from unittest.mock import patch
from .views import data_processor

class DataProcessorTestCase(TestCase):
    @patch('path.to.views.requests.get')
    def test_data_processor(self, mock_get):
        # Идеальное сочетание Django и имитаций
        mock_get.return_value.json.return_value = {'key':'value'}
        self.assertEqual(data_processor(), {'key': 'value'})

Полезные материалы

  1. GitHub – getsentry/responses: Утилита для имитации работы библиотеки Python Requests. — подробное введение в инструмент responses для замены запросов.
  2. Добро пожаловать в документацию requests-mock! — руководство по использованию requests-mock для эффективного создания имитаций запросов.
  3. Понимание библиотеки Python Mock Object – Real Python — глубокое погружение в примеры и приемы создания имитаций в Python.
  4. unittest.mock — библиотека имитаций в Python 3.12.1 документация — официальное руководство использованию unittest.mock.
  5. Как имитировать запросы и ответы? – Stack Overflow — обсуждение методов создания имитаций Python запросов для тестов на Stack Overflow.