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

В чем разница между 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

Сравнительная таблица

АспектFixtureMock
НазначениеПодготовка состоянияПодмена поведения
ОбластьРесурсы, данныеВнешние зависимости
Жизненный циклДо/после тестаНа время теста
Инструментpytestunittest.mock
ПереиспользованиеВысокое (многие тесты)Локальное (в тесте)
ОчисткаАвтоматическая (yield)Ручная или контекстный менеджер

Вывод

Fixture: когда нужно готовить общие ресурсы и состояние для группы тестов.

Mock: когда нужно изолировать код от внешних зависимостей и проверить, как он их использует.