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

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

@potapov_me

Платформа

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

Контент

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

Компания

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

Аккаунт

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

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

·ИП Потапов К.С.·Политика конфиденциальности·
Сделано с ❤️ в России
  1. Мокинг: тестируем код с API без настоящего API
mocking_real_dependencies

Мокинг: тестируем код с API без настоящего API

Изолируем тесты от внешнего мира: подменяем HTTP, файлы, время и конфигурацию. Тесты работают всегда — даже без интернета

⚠️ Урок устарел. Свежая версия в курсе pytest-junior: unittest.mock: Mock, patch, MagicMock.

Мокинг: тесты, которые работают всегда

🌐 Проблема: тесты падают без интернета, API медленные или меняют формат
✅ Решение: моки изолируют тесты — быстрые, стабильные, без внешних зависимостей

✅ Тестируем код с API без реальных HTTP запросов
✅ Работаем с файлами и временем без реальных файлов и часов
✅ Тесты проходят в самолёте, без сети и лимитов

Зачем нужны моки? Реальная боль новичков

Если тест зависит от внешнего мира (интернет, API, файлы), он будет падать из-за вещей вне вашего контроля: нет сети, сервис лежит, формат ответа изменился, закончились лимиты.

# ❌ ТЕСТ, КОТОРЫЙ ПАДАЕТ ВСЮДУ def test_fetch_user_data(): response = requests.get("https://api.example.com/users/1") # зависит от сети и API data = response.json() assert data["name"] == "John"

Моки заменяют внешнюю зависимость на предсказуемую заглушку: тест выполняется мгновенно и стабильно, даже без интернета.

# ✅ ТЕСТ С МОКАМИ — РАБОТАЕТ ВСЕГДА from unittest.mock import patch def test_fetch_user_data_with_mock(): with patch("requests.get") as mock_get: mock_get.return_value.json.return_value = { "id": 1, "name": "John", "email": "john@example.com", } user = fetch_user_data(1) assert user.name == "John" mock_get.assert_called_once_with("https://api.example.com/users/1")

unittest.mock.patch — основной инструмент

patch временно подменяет импортируемые функции/объекты и автоматически восстанавливает их после блока или теста.

patch контекстно (блок кода)

def fetch_data(): resp = requests.get("https://api.example.com/data", timeout=5) resp.raise_for_status() return resp.json() from unittest.mock import patch import requests def test_api_call(): with patch("requests.get") as mock_get: mock_get.return_value.status_code = 200 mock_get.return_value.json.return_value = {"data": "test"} result = fetch_data() assert result == {"data": "test"} mock_get.assert_called_once_with("https://api.example.com/data")

patch на весь тест (декоратор) и обработка исключений

Декоратор заменяет зависимость на весь тест; side_effect позволяет эмулировать ошибки или разные ответы.

def fetch_data(): resp = requests.get("https://api.example.com/data", timeout=5) resp.raise_for_status() return resp.json() # patch как декоратор @patch("requests.get") def test_api_call_with_decorator(mock_get): mock_get.return_value.json.return_value = {"data": "test"} result = fetch_data() assert result == {"data": "test"} # Моки с исключениями def test_api_error_handling(): with patch("requests.get") as mock_get: mock_get.side_effect = requests.RequestException("Network error") with pytest.raises(DataFetchError): fetch_data()

monkeypatch — встроенный в pytest

monkeypatch удобен для простых подмен: атрибутов, env переменных, функций. После теста всё восстанавливается автоматически.

Простая подмена функций

Подменяем requests.get на свою функцию, чтобы не ходить в сеть. Фикстура monkeypatch автоматически доступна во всех тестах pytest и откатывает изменения после выполнения.

def test_with_monkeypatch(monkeypatch): def fake_get(url): return {"mocked": True} monkeypatch.setattr(requests, "get", fake_get) result = fetch_data() assert result["mocked"] is True # 🎯 Автоматическое восстановление после теста

Подмена переменных окружения

Используем setenv, чтобы код читал нужные значения без правки реальных переменных окружения.

def test_env_variables(monkeypatch): monkeypatch.setenv("API_KEY", "test-key") config = load_config() assert config.api_key == "test-key"

Подмена атрибутов объектов

Можно подменять методы/флаги на уже созданных объектах для проверки разных состояний.

def test_object_attributes(monkeypatch): user = User(name="test") monkeypatch.setattr(user, "is_admin", lambda: True) assert user.is_admin() is True

Реальные сценарии мокинга

Эти примеры показывают типичные задачи новичков: HTTP вызовы, работа с файлами, временем и БД без реальных зависимостей.

1. HTTP запросы

Заменяем вызовы HTTP-клиента на заглушки, чтобы не зависеть от сети и формата ответа.

@patch("httpx.get") def test_fetch_weather(mock_get): mock_get.return_value.json.return_value = { "temperature": 25, "condition": "sunny", "city": "London", } weather = fetch_weather("London") assert weather.temperature == 25 assert weather.condition == "sunny"

2. Работа с файлами

Подменяем open реальным временным файлом, чтобы не создавать/удалять файлы руками.

def test_process_config(tmp_path): config_file = tmp_path / "config.yaml" config_file.write_text("api_key: test-key\ndebug: true") # Подменяем open, чтобы load_config читал наш временный файл with patch("builtins.open", return_value=config_file.open()): config = load_config() assert config["api_key"] == "test-key"

3. Работа со временем

Мокаем datetime чтобы тестировать логику для любой даты/времени.

from unittest.mock import patch import datetime def test_is_weekend(): with patch("datetime.datetime") as mock_datetime: mock_datetime.now.return_value.weekday.return_value = 5 # Суббота assert is_weekend() is True mock_datetime.now.return_value.weekday.return_value = 0 # Понедельник assert is_weekend() is False

4. База данных

Мокаем ORM-запрос, чтобы не поднимать реальную БД и проверять обработку данных.

@patch("database.models.User.query") def test_get_user_by_email(mock_query): mock_user = Mock() mock_user.email = "test@example.com" mock_query.filter_by.return_value.first.return_value = mock_user user = get_user_by_email("test@example.com") assert user.email == "test@example.com"

patch vs monkeypatch: когда что использовать

  • patch (unittest.mock) — удобно для модульных импортов, контекстных менеджеров и декораторов; даёт методы assert_called_with, side_effect.
  • monkeypatch (pytest) — быстрое внедрение зависимостей (setattr, setenv), автоклейнап; проще для новичков.

❌ Анти-паттерны: как НЕ использовать моки

Мок всего подряд

def test_complex_function(): with patch("module.api.call"), \ patch("module.database.query"), \ patch("module.file.read"), \ patch("module.config.get"), \ patch("module.time.now"): result = complex_function() # 🚨 Тест проверяет моки, а не логику # Итог: сложно понять, что реально проверяется, тест хрупкий и бесполезный

Слишком строгие проверки

def test_over_specified(): with patch("requests.get") as mock_get: fetch_data() mock_get.assert_called_once_with( "https://api.example.com/data", headers={"Authorization": "Bearer token"}, timeout=30, params={"page": 1, "limit": 50}, # 🚨 хрупко ) # Итог: малейшее изменение параметров ломает тест, хотя логика может быть корректной
def test_flexible(): with patch("requests.get") as mock_get: fetch_data() args, kwargs = mock_get.call_args assert args[0].startswith("https://api.example.com") assert "Authorization" in kwargs.get("headers", {})

Мок приватной логики

def test_mock_internals(): with patch("my_module._private_helper"): result = public_function() # Итог: тест проверяет детали реализации, не поведение
def test_public_interface(): with patch("my_module.external_dependency"): result = public_function()

Практика: создайте свои первые моки

Задача 1: отправка email

def send_notification(email, message): import smtplib with smtplib.SMTP("smtp.example.com") as server: server.sendmail("noreply@example.com", email, f"Subject: Notification\n\n{message}")

Напишите тест с моком для smtplib.SMTP:

  • Проверить, что SMTP создаётся с правильным хостом
  • Проверить, что sendmail вызывается с нужными аргументами

Задача 2: функция с временем

def is_morning(): from datetime import datetime hour = datetime.now().hour return 6 <= hour < 12

Напишите тест с моком для datetime:

  • Утро (возвращает True)
  • Вечер (возвращает False)

Подсказка: используйте patch для внешних вызовов (SMTP, datetime) и проверяйте, что код ведёт себя правильно без реальных сетевых/временных зависимостей.

🚀 Шпаргалка: моки в действии

Когда использовать моки

  • 🌐 HTTP запросы — тесты не должны зависеть от внешних API
  • 📁 Файлы — не создавайте реальные файлы в тестах
  • ⏰ Время — тестируйте логику для любого момента
  • 🗄️ Базы данных — изолируйте от реальной БД
  • 🔧 Внешние сервисы — email, SMS, платежи

Инструменты

Коротко о главных способах мокинга в Python/pytest:

# unittest.mock.patch with patch("module.function") as mock: mock.return_value = "result" # pytest monkeypatch def test_example(monkeypatch): monkeypatch.setattr(module, "function", lambda: "result") # Mock объекты from unittest.mock import Mock mock_obj = Mock() mock_obj.method.return_value = "result"

Полезные методы mock объектов

# Возвращаемое значение mock.return_value = {"status": "ok"} # fetch_data() вернёт этот словарь # Исключение или последовательность значений mock.side_effect = TimeoutError() # simulate timeout mock.side_effect = [1, 2, 3] # три вызова: 1, потом 2, потом 3 # Проверки вызовов mock.assert_called_once() # убедиться, что отправили email только один раз mock.assert_called_with("url") # убедиться, что запрос ушёл на правильный URL mock.call_count # сколько раз дернули — например, ретраи

Чеклист качественных моков

  • Мокаем только внешние зависимости
  • Тесты изолированы — без сети/БД/файлов
  • Проверяем поведение, не только возвращаемые значения
  • Минимальный scope моков
  • Тесты быстрые — никаких реальных запросов
  • Восстанавливаем состояние (context manager/monkeypatch)

Поздравляю! Теперь ваши тесты работают всегда и везде — без внешних сюрпризов. 🎯

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

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

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

Далее: Мини-проект: интеграционное тестирование