Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Что такое мокирование?
Мокирование (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')
Заключение
Мокирование — это:
- Техника изоляции при юнит-тестировании
- Способ тестировать без внешних зависимостей
- Инструмент для быстрых и надёжных тестов
- Не замена интеграционным тестам
Правильное использование моков делает тесты быстрыми, надёжными и не зависящими от внешних сервисов.