← Назад к вопросам
В чем разница между mock и fixture в pytest?
1.8 Middle🔥 161 комментариев
#Python Core#Тестирование
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
mock vs fixture в pytest
Это два разных инструмента для разных задач при тестировании. Mock подменяет поведение объектов, fixture — готовит данные и состояние для тестов.
Fixture — подготовка состояния
Fixture — это функция, которая подготавливает окружение для теста (данные, конфигурацию, соединения):
import pytest
@pytest.fixture
def user_data():
return {"id": 1, "name": "John", "email": "john@example.com"}
@pytest.fixture
def database():
# Подготовка
db = connect_to_db()
db.create_schema()
yield db
# Очистка
db.drop_schema()
db.close()
def test_user_creation(user_data, database):
user = database.create_user(user_data)
assert user.id == 1
assert user.name == "John"
Mock — подмена поведения
Mock из unittest.mock подменяет объекты и отслеживает вызовы:
from unittest.mock import Mock, patch
def test_with_mock():
# Создаём mock объект
mock_api = Mock()
mock_api.fetch_user.return_value = {"id": 1, "name": "John"}
# Используем в коде
result = mock_api.fetch_user(1)
assert result["name"] == "John"
# Проверяем, что метод был вызван
mock_api.fetch_user.assert_called_once_with(1)
assert mock_api.fetch_user.call_count == 1
def test_with_patch():
with patch('requests.get') as mock_get:
mock_get.return_value.status_code = 200
mock_get.return_value.json.return_value = {"data": "test"}
response = requests.get("https://api.example.com")
assert response.status_code == 200
mock_get.assert_called_with("https://api.example.com")
Основные различия
1. Назначение
# Fixture — подготовка данных
@pytest.fixture
def api_client():
return APIClient(base_url="http://localhost:8000")
def test_api(api_client):
response = api_client.get("/users")
assert response.status_code == 200
# Mock — подмена внешних зависимостей
def test_api_with_mock():
with patch('requests.get') as mock_get:
mock_get.return_value.json.return_value = {"users": []}
# Тестируем логику без реального запроса
2. Область применения
# Fixture: базы данных, файлы, соединения
@pytest.fixture
def temp_file():
import tempfile
with tempfile.NamedTemporaryFile(mode='w') as f:
yield f
# Очистка
@pytest.fixture
def logger():
import logging
return logging.getLogger("test")
# Mock: внешние API, системные вызовы, сложные объекты
from unittest.mock import Mock
def test_with_external_service():
mock_service = Mock()
mock_service.send_email.return_value = True
# Тестируем без реального отправления письма
assert mock_service.send_email("test@example.com") == True
Комбинирование fixture и mock
Часто нужны оба подхода:
import pytest
from unittest.mock import Mock, patch
@pytest.fixture
def user_service():
return UserService()
@pytest.fixture
def mock_email_client():
return Mock()
def test_user_registration(user_service, mock_email_client):
# Fixture готовит сервис
# Mock подменяет внешний сервис email
with patch('user_service.email_client', mock_email_client):
user = user_service.register("john@example.com")
# Проверяем, что email был отправлен
mock_email_client.send.assert_called_once()
assert user is not None
Разные типы mock
from unittest.mock import Mock, MagicMock, patch, call
# Mock — базовый mock
mock = Mock()
mock.method(1, 2, 3)
assert mock.method.call_count == 1
assert mock.method.call_args == call(1, 2, 3)
# MagicMock — с поддержкой магических методов
magic_mock = MagicMock()
result = len(magic_mock) # __len__ поддерживается
assert result == 0
# patch — подмена функции или класса на время теста
with patch('module.expensive_function') as mock_func:
mock_func.return_value = 42
result = expensive_function() # Вернёт 42
# side_effect — для более сложного поведения
mock = Mock(side_effect=[1, 2, 3])
assert mock() == 1
assert mock() == 2
assert mock() == 3
mock_exception = Mock(side_effect=ValueError("Error"))
try:
mock_exception()
except ValueError:
pass
Лучшие практики
Используй fixture для состояния
# ✅ Правильно
@pytest.fixture
def authenticated_user(database):
user = database.create_user({"email": "test@example.com"})
yield user
database.delete_user(user.id)
def test_user_profile(authenticated_user):
assert authenticated_user.email == "test@example.com"
Используй mock для внешних зависимостей
# ✅ Правильно
from unittest.mock import patch
def test_payment_processing():
with patch('payment_gateway.process') as mock_payment:
mock_payment.return_value = {"status": "success"}
# Тестируем логику без реального платежа
result = process_order(100)
assert result["status"] == "success"
Fixture для сложной подготовки
@pytest.fixture
def api_with_data(api_client):
# Подготавливаем тестовые данные
user = api_client.create_user({"name": "Test"})
project = api_client.create_project({"owner_id": user.id})
yield {"user": user, "project": project}
def test_project_access(api_with_data):
assert api_with_data["project"].owner_id == api_with_data["user"].id
Сравнительная таблица
| Аспект | Fixture | Mock |
|---|---|---|
| Назначение | Подготовка состояния | Подмена поведения |
| Область | Ресурсы, данные | Внешние зависимости |
| Жизненный цикл | До/после теста | На время теста |
| Инструмент | pytest | unittest.mock |
| Переиспользование | Высокое (многие тесты) | Локальное (в тесте) |
| Очистка | Автоматическая (yield) | Ручная или контекстный менеджер |
Вывод
Fixture: когда нужно готовить общие ресурсы и состояние для группы тестов.
Mock: когда нужно изолировать код от внешних зависимостей и проверить, как он их использует.