Мокирование запросов с помощью Python: использование mock package
Быстрый ответ
Сначала можно воспользоваться библиотекой requests-mock
. Она позволяет легко формировать заранее определенные ответы для тестов:
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' заранее заготовленное содержимое. Таким образом, мы ускоряем работу тестов и делаем их надёжными, всё это без обращения к реальному серверу.
[AsideBanner]
Альтернативы для управления запросами
Если вам необходимы более детализированные настройки, обратите внимание на responses
. Этот инструмент предлагает удобный способ замены ответов в вашем приложении:
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
.
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'
Визуализация
Давайте представим процесс мокирования как театральную постановку:
Сцена (💻): Ваше Приложение
Актер (🎭): requests
Роль (📜): GET 'https://api.example.com/data'
Для этого подключим дублёра (👥 мок):
from unittest.mock import MagicMock
requests.get = MagicMock(return_value='Имитированный Контент')
Вот как проходит взаимодействие:
💻 --> 🎭: "Сделай GET-запрос!"
👥 (Мок): "Получите вот этот 'Имитированный Контент'!"
Суммируя сценарий:
- Приложение (💻) уверено, что общается с настоящим «requests»,
- Но дублёр (👥) играет свою роль и предоставляет имитированное исполнение.
Создание ваших имитаций
Рассмотрим более сложные сценарии с примерами создания имитаций:
Тестирование нескольких URL
Для тестирования нескольких URL с разными HTTP-методами отлично подойдет HTTPretty
.
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
позволяет имитировать вызовы в модульных тестах:
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:
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'})
Полезные материалы
- GitHub – getsentry/responses: Утилита для имитации работы библиотеки Python Requests. — подробное введение в инструмент responses для замены запросов.
- Добро пожаловать в документацию requests-mock! — руководство по использованию requests-mock для эффективного создания имитаций запросов.
- Понимание библиотеки Python Mock Object – Real Python — глубокое погружение в примеры и приемы создания имитаций в Python.
- unittest.mock — библиотека имитаций в Python 3.12.1 документация — официальное руководство использованию unittest.mock.
- Как имитировать запросы и ответы? – Stack Overflow — обсуждение методов создания имитаций Python запросов для тестов на Stack Overflow.