Перейти к основному контенту
Tech Path Finder
КурсыИнтервьюКод-ревьюБлог
Tech Path Finder

Персонализированный путеводитель в IT. Квизы, мок-интервью, код ревью и аналитика прогресса.

@potapov_me

Платформа

  • Курсы
  • Прогресс
  • Мок-интервью
  • Код ревью
  • Тренажёр переговоров
  • Закладки

Контент

  • Блог
  • Главная
  • Обратная связь

Компания

  • О проекте
  • Тарифы
  • Условия использования
  • Конфиденциальность
  • Согласие на обработку данных
  • Cookie
  • Реквизиты

Аккаунт

  • Войти
  • Зарегистрироваться
  • Профиль

© 2026 Tech Path Finder. Все права защищены.

·ИП Потапов К.С.·Политика конфиденциальности·
Сделано с ❤️ в России
  1. Мокирование HTTP: requests-mock
mocking_http

Мокирование HTTP: requests-mock

Тестирование кода с HTTP-запросами без реальной сети. requests-mock для GET/POST

У вас есть класс GitHubClient который делает HTTP-запросы к GitHub API. Как протестировать БЕЗ реальных запросов к GitHub?

Цель: Научиться мокировать HTTP-запросы с помощью requests-mock.

Вы точно готовы?

Убедитесь, что умеете:

# Создавать моки и патчить зависимости from unittest.mock import Mock, patch @patch("module.requests") def test_api(mock_requests): mock_requests.get.return_value.json.return_value = {"data": 123}

Если unittest.mock непонятен — вернитесь к unittest.mock основы.

Проблема: патчить requests неудобно

Код с HTTP-запросами

# src/github_client.py import requests class GitHubClient: def __init__(self, token): self.token = token self.base_url = "https://api.github.com" def get_user(self, username): """Получает данные пользователя""" response = requests.get( f"{self.base_url}/users/{username}", headers={"Authorization": f"token {self.token}"} ) return response.json() def create_issue(self, repo, title, body): """Создаёт issue в репозитории""" response = requests.post( f"{self.base_url}/repos/{repo}/issues", headers={"Authorization": f"token {self.token}"}, json={"title": title, "body": body} ) return response.json()

Тест с unittest.mock (многословно)

@patch("src.github_client.requests") def test_get_user(mock_requests): # ❌ Много кода для простого теста mock_response = Mock() mock_response.json.return_value = { "login": "alice", "name": "Alice Smith", "public_repos": 42 } mock_requests.get.return_value = mock_response client = GitHubClient(token="fake-token") user = client.get_user("alice") assert user["login"] == "alice"

Проблемы:

  • ❌ Многословно (нужно создавать Mock, настраивать response)
  • ❌ Сложно проверять query params, headers
  • ❌ Не видно какой URL мокируется

Решение: requests-mock

Установка

pip install requests-mock

Базовое использование

import requests import requests_mock def test_get_user(): with requests_mock.Mocker() as m: # Мокируем GET запрос m.get("https://api.github.com/users/alice", json={ "login": "alice", "name": "Alice Smith", "public_repos": 42 }) # Реальный вызов requests.get() попадёт в мок! response = requests.get("https://api.github.com/users/alice") data = response.json() assert data["login"] == "alice" assert data["public_repos"] == 42

✅ Просто и понятно!

Использование как фикстура (рекомендуется)

import pytest import requests_mock @pytest.fixture def mock_requests(): """Фикстура для мокирования HTTP""" with requests_mock.Mocker() as m: yield m def test_get_user(mock_requests): """Тест с фикстурой""" mock_requests.get("https://api.github.com/users/alice", json={ "login": "alice", "public_repos": 42 }) client = GitHubClient(token="fake-token") user = client.get_user("alice") assert user["login"] == "alice"

Мокирование разных HTTP методов

def test_http_methods(mock_requests): # GET mock_requests.get("https://api.example.com/users", json={"users": []}) # POST mock_requests.post("https://api.example.com/users", json={"id": 1}) # PUT mock_requests.put("https://api.example.com/users/1", json={"updated": True}) # DELETE mock_requests.delete("https://api.example.com/users/1", status_code=204) # PATCH mock_requests.patch("https://api.example.com/users/1", json={"patched": True}) # Любой метод mock_requests.request("OPTIONS", "https://api.example.com/users", text="OK")

Проверка запросов

Проверка headers

def test_authorization_header(mock_requests): """Проверяем что токен отправляется в заголовке""" mock_requests.get("https://api.github.com/users/alice", json={"login": "alice"}) client = GitHubClient(token="secret-token") client.get_user("alice") # Проверяем что запрос был с правильным header history = mock_requests.request_history[0] assert history.headers["Authorization"] == "token secret-token"

Проверка query parameters

def test_query_params(mock_requests): """Проверяем query параметры""" mock_requests.get("https://api.github.com/search/repositories", json={ "items": [] }) # Делаем запрос с query params response = requests.get("https://api.github.com/search/repositories", params={ "q": "pytest", "sort": "stars" }) # Проверяем что параметры отправлены history = mock_requests.request_history[0] assert history.qs["q"] == ["pytest"] # qs = query string assert history.qs["sort"] == ["stars"]

Проверка JSON body

def test_create_issue(mock_requests): """Проверяем JSON body в POST запросе""" mock_requests.post("https://api.github.com/repos/user/repo/issues", json={ "id": 123, "state": "open" }) client = GitHubClient(token="token") issue = client.create_issue("user/repo", "Bug report", "Something is broken") # Проверяем отправленные данные history = mock_requests.request_history[0] assert history.json() == { "title": "Bug report", "body": "Something is broken" }

request_history: все запросы

def test_multiple_requests(mock_requests): """Проверяем несколько запросов""" mock_requests.get("https://api.example.com/users/1", json={"id": 1}) mock_requests.get("https://api.example.com/users/2", json={"id": 2}) requests.get("https://api.example.com/users/1") requests.get("https://api.example.com/users/2") # История всех запросов assert len(mock_requests.request_history) == 2 assert mock_requests.request_history[0].url == "https://api.example.com/users/1" assert mock_requests.request_history[1].url == "https://api.example.com/users/2"

Симуляция ошибок

HTTP ошибки (404, 500)

def test_user_not_found(mock_requests): """Тест обработки 404""" # Мокируем 404 ответ mock_requests.get("https://api.github.com/users/nonexistent", status_code=404) client = GitHubClient(token="token") with pytest.raises(requests.HTTPError): client.get_user("nonexistent") def test_server_error(mock_requests): """Тест обработки 500""" mock_requests.get("https://api.github.com/users/alice", status_code=500) client = GitHubClient(token="token") with pytest.raises(requests.HTTPError): client.get_user("alice")

Timeout

import requests def test_timeout(mock_requests): """Тест обработки timeout""" # Симулируем timeout mock_requests.get("https://api.github.com/users/alice", exc=requests.Timeout) client = GitHubClient(token="token") with pytest.raises(requests.Timeout): client.get_user("alice")

Connection error

def test_connection_error(mock_requests): """Тест обработки connection error""" mock_requests.get("https://api.github.com/users/alice", exc=requests.ConnectionError) client = GitHubClient(token="token") with pytest.raises(requests.ConnectionError): client.get_user("alice")

Custom response body для ошибок

def test_api_error_message(mock_requests): """Проверяем обработку сообщения об ошибке""" mock_requests.get("https://api.github.com/users/alice", status_code=403, json={ "message": "API rate limit exceeded" }) client = GitHubClient(token="token") try: client.get_user("alice") except requests.HTTPError as e: error_data = e.response.json() assert "rate limit" in error_data["message"].lower()

URL matching: гибкая настройка

Exact match (точное совпадение)

def test_exact_url(mock_requests): mock_requests.get("https://api.example.com/users/123", json={"id": 123}) # ✅ Работает requests.get("https://api.example.com/users/123") # ❌ Не работает (другой URL) # requests.get("https://api.example.com/users/456")

Regex match

import re def test_regex_url(mock_requests): # Мокируем любой URL вида /users/{id} mock_requests.get(re.compile(r"https://api\.example\.com/users/\d+"), json={"ok": True}) # ✅ Оба работают requests.get("https://api.example.com/users/123") requests.get("https://api.example.com/users/456")

ANY (любой URL)

import requests_mock def test_any_url(mock_requests): # Мокируем ЛЮБОЙ GET запрос mock_requests.get(requests_mock.ANY, json={"fallback": True}) requests.get("https://api.example.com/anything") requests.get("https://another-api.com/something") # Все запросы попадут в этот мок

Практический пример: WeatherService

# src/weather_service.py import requests class WeatherService: def __init__(self, api_key): self.api_key = api_key self.base_url = "https://api.openweathermap.org/data/2.5" def get_current_weather(self, city): """Получает текущую погоду""" response = requests.get( f"{self.base_url}/weather", params={"q": city, "appid": self.api_key, "units": "metric"} ) response.raise_for_status() return response.json() def get_forecast(self, city, days=5): """Получает прогноз погоды""" response = requests.get( f"{self.base_url}/forecast", params={"q": city, "appid": self.api_key, "units": "metric", "cnt": days} ) response.raise_for_status() return response.json() # tests/test_weather_service.py import pytest import requests_mock import requests @pytest.fixture def mock_requests(): with requests_mock.Mocker() as m: yield m def test_get_current_weather(mock_requests): """Тест получения текущей погоды""" # Arrange: мокируем API mock_requests.get("https://api.openweathermap.org/data/2.5/weather", json={ "main": {"temp": 15.5, "humidity": 80}, "weather": [{"description": "clear sky"}] }) # Act service = WeatherService(api_key="fake-key") weather = service.get_current_weather("Moscow") # Assert assert weather["main"]["temp"] == 15.5 assert weather["weather"][0]["description"] == "clear sky" # Verify request history = mock_requests.request_history[0] assert history.qs["q"] == ["Moscow"] assert history.qs["appid"] == ["fake-key"] assert history.qs["units"] == ["metric"] def test_get_forecast(mock_requests): """Тест получения прогноза""" mock_requests.get("https://api.openweathermap.org/data/2.5/forecast", json={ "list": [ {"dt": 1234567890, "main": {"temp": 15}}, {"dt": 1234567891, "main": {"temp": 16}}, {"dt": 1234567892, "main": {"temp": 14}}, ] }) service = WeatherService(api_key="fake-key") forecast = service.get_forecast("Moscow", days=3) assert len(forecast["list"]) == 3 assert forecast["list"][0]["main"]["temp"] == 15 def test_weather_api_error(mock_requests): """Тест обработки ошибки API""" mock_requests.get( "https://api.openweathermap.org/data/2.5/weather", status_code=401, json={"message": "Invalid API key"} ) service = WeatherService(api_key="invalid-key") with pytest.raises(requests.HTTPError) as exc_info: service.get_current_weather("Moscow") assert exc_info.value.response.status_code == 401 def test_weather_timeout(mock_requests): """Тест обработки timeout""" mock_requests.get( "https://api.openweathermap.org/data/2.5/weather", exc=requests.Timeout ) service = WeatherService(api_key="fake-key") with pytest.raises(requests.Timeout): service.get_current_weather("Moscow")

✅ Все тесты работают мгновенно БЕЗ реального API!

Что вы изучили

  • requests-mock — удобное мокирование HTTP
  • HTTP методы — GET, POST, PUT, DELETE, PATCH
  • Проверка запросов — headers, query params, JSON body
  • request_history — история всех запросов
  • Симуляция ошибок — 404, 500, timeout, connection error
  • URL matching — exact, regex, ANY

Проверьте свои знания

Вопросы ещё не добавлены

Вопросы для этой подтемы ещё не добавлены.

Далее: Мокирование времени и файлов