← Назад к вопросам

Что такое мокирование?

3.0 Senior🔥 131 комментариев
#DevOps и инфраструктура#Django

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что такое мокирование?

Мокирование (mocking) — это техника unit-тестирования, при которой реальные объекты, функции или внешние сервисы замещаются на их имитационные версии (mock'и). Это позволяет тестировать код в изоляции, без зависимостей от внешних систем.

Основная идея

Мок — это fake-объект, который имитирует поведение реального объекта. Вместо того чтобы делать реальный HTTP запрос, обращаться к базе данных или вызывать файловую систему, мы используем имитацию, которая возвращает нам заранее определённые значения.

Типы тестовых двойников

Существует несколько типов fake-объектов:

1. Mock — проверяет поведение

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

from unittest.mock import Mock, patch

# Создаём мок
mail_service = Mock()

# Вызваем функцию
def send_notification(user, message):
    mail_service.send(user.email, message)

send_notification(user, "Hello")

# Проверяем, был ли вызван с нужными аргументами
mail_service.send.assert_called_once_with(user.email, "Hello")

2. Stub — возвращает значения

Стаб просто возвращает заранее определённые данные:

from unittest.mock import Mock

# Стаб, который возвращает значение
db = Mock()
db.get_user.return_value = {"id": 1, "name": "John"}

# Вызов
user = db.get_user(1)
print(user)  # {'id': 1, 'name': 'John'}

3. Spy — следит и пропускает реальный вызов

Spy вызывает реальный метод, но отслеживает вызовы:

from unittest.mock import patch

# Обёртка над реальной функцией
with patch('module.real_function') as spy:
    spy.side_effect = lambda x: x * 2  # Реальное поведение
    result = spy(5)
    assert spy.called
    assert result == 10

4. Dummy — просто объект для передачи

from unittest.mock import Mock

# Dummy, который не используется
logger = Mock()
# Передаём в функцию, но не проверяем
process_data(data, logger)

Практические примеры

Пример 1: Мокирование внешнего API

import requests
from unittest.mock import patch, Mock

def get_weather(city):
    response = requests.get(f'https://weather.api.com/{city}')
    return response.json()['temperature']

# Тест с мокированием
@patch('requests.get')
def test_get_weather(mock_get):
    # Готовим mock-ответ
    mock_response = Mock()
    mock_response.json.return_value = {'temperature': 25}
    mock_get.return_value = mock_response
    
    # Вызываем функцию
    temp = get_weather('Moscow')
    
    # Проверяем результат
    assert temp == 25
    
    # Проверяем, что был сделан правильный запрос
    mock_get.assert_called_once_with('https://weather.api.com/Moscow')

Пример 2: Мокирование методов класса

from unittest.mock import patch, MagicMock

class UserService:
    def get_user(self, user_id):
        # Получает пользователя из БД
        pass
    
    def notify_user(self, user_id, message):
        user = self.get_user(user_id)
        send_email(user.email, message)

# Тест
def test_notify_user():
    service = UserService()
    
    # Мокируем метод get_user
    with patch.object(service, 'get_user') as mock_get:
        mock_get.return_value = MagicMock(email='test@example.com')
        
        with patch('send_email') as mock_send:
            service.notify_user(1, 'Hello')
            
            # Проверяем оба вызова
            mock_get.assert_called_once_with(1)
            mock_send.assert_called_once_with('test@example.com', 'Hello')

Пример 3: Мокирование с помощью pytest-mock

import pytest
from myapp import UserService

def test_create_user(mocker):
    # mocker — fixture из pytest-mock
    mock_db = mocker.patch('myapp.db')
    mock_db.insert.return_value = 1
    
    service = UserService()
    user_id = service.create_user('john', 'john@example.com')
    
    assert user_id == 1
    mock_db.insert.assert_called_once()

Типичные проблемы и как их избежать

Проблема 1: Чрезмерное мокирование

# ПЛОХО — мокируем всё подряд
def test_calculate():
    with patch('math.sqrt'):
        with patch('math.pow'):
            with patch('math.ceil'):
                result = calculate(5)

# ХОРОШО — мокируем только внешние зависимости
def test_calculate():
    with patch('external_api.call'):
        result = calculate(5)
        assert result == expected

Проблема 2: Мокирование внутренних деталей

# ПЛОХО — тест привязан к деталям реализации
@patch('mymodule.helper_function')
def test_process(mock_helper):
    process_data(data)  # Проверяем внутренние функции

# ХОРОШО — тестируем поведение через интерфейс
def test_process():
    result = process_data(data)
    assert result == expected

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

ИСПОЛЬЗУЙ моки для:

  • Внешних API (HTTP запросы)
  • Базы данных в unit-тестах
  • Файловой системы
  • Библиотек, которые медленные
  • Недетерминированных функций (случайные числа, время)
  • Сложных объектов, которые долго инициализировать

НЕ ИСПОЛЬЗУЙ моки для:

  • Внутренней логики вашего кода
  • Полной замены интеграционных тестов
  • Чтения результатов, если можно использовать реальный объект

Инструменты

Python:

  • unittest.mock — встроенный модуль
  • pytest-mock — удобнее с pytest
  • responses — специально для HTTP
  • mongomock — мок для MongoDB
# unittest.mock
from unittest.mock import Mock, patch, MagicMock

# pytest-mock
def test_something(mocker):
    mocker.patch('module.function')

Заключение

Мокирование — это:

  • Техника изоляции при юнит-тестировании
  • Способ тестировать без внешних зависимостей
  • Инструмент для быстрых и надёжных тестов
  • Не замена интеграционным тестам

Правильное использование моков делает тесты быстрыми, надёжными и не зависящими от внешних сервисов.

Что такое мокирование? | PrepBro