В чем разница между mock и fixture в pytest?

Ответ

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

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

  • Fixture (Фикстура) — это функция, которая предоставляет данные, объекты или настраивает состояние для тестов. Она используется для подготовки окружения и устранения дублирования кода (принцип DRY — Don't Repeat Yourself).

Сравнение:

Аспект Mock Fixture
Основная цель Изоляция кода, имитация поведения Подготовка данных и состояния, переиспользование кода
Что делает? Подменяет реальный объект Предоставляет готовый объект или данные
Источник unittest.mock или pytest-mock (обертка mocker) Декоратор @pytest.fixture
Пример Заменить обращение к внешнему API на возврат заранее заданного JSON Создать подключение к тестовой БД или вернуть объект пользователя

Пример совместного использования

Представим, что у нас есть функция, которая получает данные пользователя из БД и возвращает его возраст.

import pytest

# ФИКСТУРА: предоставляет тестовые данные
@pytest.fixture
def sample_user_data():
    """Эта фикстура просто возвращает словарь с данными."""
    return {"id": 1, "name": "Alice", "age": 30}

# Функция, которую мы хотим протестировать
# (предполагается, что db.get_user обращается к реальной БД)
from my_app import logic, db

def test_get_user_age(mocker, sample_user_data):
    # МОК: подменяем функцию обращения к БД
    # Теперь при вызове db.get_user(1) не будет реального запроса к БД,
    # а вернется значение из фикстуры sample_user_data.
    mocker.patch('my_app.db.get_user', return_value=sample_user_data)

    # Вызываем тестируемую логику
    age = logic.get_user_age(1)

    # Проверяем, что наша логика правильно извлекла возраст
    assert age == 30

В этом примере:

  1. Фикстура sample_user_data подготовила нам тестовые данные.
  2. Мок mocker.patch изолировал тест от реальной базы данных, заставив функцию db.get_user вернуть наши данные.

Ответ 18+ 🔞

Давай разберём эту дичь, чтобы наконец перестать путать мок и фикстуру, как последние распиздяи. Это же две разные сущности, ёпта, как хуй и пальто!

Мок (Mock) — это, грубо говоря, подставная шлюха для твоего кода. Её задача — подменить какую-то реальную зависимость (типа вызова к базе данных, внешнему API или файловой системе), чтобы твой тест не пошёл нахуй из-за этих внешних штук. Ты её настраиваешь: «Вот тут верни такой-то JSON», «А тут кинь исключение». Всё контролируешь.

Фикстура (Fixture) — это, блядь, не подмена, а подготовка. Это такая заботливая мамка, которая перед тестом накрывает тебе стол: создаёт тестовые данные, поднимает временную базу, открывает файл — чтобы ты не повторял этот пиздец в каждом тесте. Принцип DRY, ёбана!

Короче, табличка для особо одарённых:

Что за хуйня? Mock (Мок) Fixture (Фикстура)
Зачем нужна? Изолировать код, сымитировать поведение Подготовить данные/состояние, убрать повторения
Что делает? Подменяет реальный объект на свою подставу Даёт тебе готовый объект или данные
Откуда брать? unittest.mock или pytest-mock (обёртка) Декоратор @pytest.fixture
Пример из жизни Заменить запрос к API на свой canned response Создать объект пользователя для всех тестов

Пример, где они ебутся вместе

Смотри, есть у нас функция, которая лезет в базу за пользователем и возвращает его возраст. Нам надо её протестировать, но реальную базу не трогать, а то она нам наебнется посреди ночи.

import pytest

# ФИКСТУРА: просто готовит нам данные, как милая девушка
@pytest.fixture
def sample_user_data():
    """Эта фикстура просто возвращает словарь с данными."""
    return {"id": 1, "name": "Alice", "age": 30}

# Допустим, у нас есть модуль logic с тестируемой функцией и модуль db для работы с БД
from my_app import logic, db

def test_get_user_age(mocker, sample_user_data):
    # МОК: а вот тут начинается подстава! Говорим: "Функция db.get_user, иди нахуй,
    # когда тебя вызовут с аргументом 1, верни не то, что в базе, а то, что нам дала фикстура."
    mocker.patch('my_app.db.get_user', return_value=sample_user_data)

    # Вызываем нашу логику. Она думает, что ходит в базу, а на самом деле её уже наебали.
    age = logic.get_user_age(1)

    # И проверяем, что она не обосралась и правильно возраст достала.
    assert age == 30

Что тут произошло, блядь?

  1. Фикстура sample_user_data наготовила нам тестовых данных — словарик с Алисой.
  2. Мок mocker.patch подсунул нашей функции подмену: вместо реального запроса к базе — наши приготовленные данные. Полная изоляция, ебать!
  3. Тест проходит в стерильных условиях, и мы все довольны.

Вот и вся магия. Не путай, а то получишь пизды от коллег на код-ревью.