Что такое фикстура (fixture) в контексте тестирования?

Ответ

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

Основные типы фикстур в pytest:

  • @pytest.fixture — базовая фикстура, создающая объект для передачи в тест.
  • scope='session' — фикстура создается один раз за всю сессию тестирования (например, подключение к БД).
  • scope='module' — создается один раз на модуль.
  • scope='class' — создается один раз на класс тестов.
  • scope='function' (по умолчанию) — создается заново для каждой тестовой функции.
  • autouse=True — фикстура запускается автоматически, без явного указания в аргументах теста.

Практический пример: подготовка и очистка тестовых данных в БД

import pytest
import psycopg2

@pytest.fixture(scope="function")
def test_user(db_connection):
    """Фикстура создает тестового пользователя и удаляет его после теста."""
    cursor = db_connection.cursor()
    # Setup: создаем пользователя
    cursor.execute(
        "INSERT INTO users (username, email) VALUES (%s, %s) RETURNING id;",
        ("test_fixture_user", "test@example.com")
    )
    user_id = cursor.fetchone()[0]
    db_connection.commit()
    yield user_id  # Передаем ID в тест
    # Teardown: удаляем пользователя после теста
    cursor.execute("DELETE FROM users WHERE id = %s;", (user_id,))
    db_connection.commit()
    cursor.close()

def test_user_creation(test_user):
    """Тест использует фикстуру `test_user`, получая ID созданного пользователя."""
    # Здесь можно проверить, что пользователь доступен в системе
    assert test_user is not None
    # После выполнения теста фикстура автоматически выполнит teardown-часть (DELETE).

Фикстуры — это мощный инструмент для управления зависимостями тестов, позволяющий избегать дублирования кода setup/teardown.

Ответ 18+ 🔞

А, фикстуры, блядь! Ну это ж классика, ёпта! Сейчас объясню на пальцах, чтобы даже мартышлюшка поняла.

Представь, ты каждый раз перед тестом, как идиот, вручную настраиваешь одно и то же: базу данных поднимаешь, тестового юзера создаёшь, конфиги читаешь. А потом после теста всё это добро надо ещё и прибрать, чтобы следующий тест не наступил в это говно. Задолбаешься, блядь, через неделю.

Так вот, фикстура — это как твой личный раб-дворецкий для тестов. Ты ему один раз говоришь: «Слушай сюда, ушастый, вот тебе инструкция, как приготовить мне чистенькую тестовую песочницу». А он тебе её каждый раз по первому требованию подаёт, да ещё и после использования сам всё за собой убирает, в рот меня чих-пых! Цель — чтобы тесты были повторяемыми и изолированными, то есть не зависели друг от друга и от погоды на Марсе.

Основные виды этих самых дворецких в pytest:

  • @pytest.fixture — базовый раб. Сказал «подай объект» — он подал.
  • scope='session' — раб на всю тусовку. Создался один раз в начале (типа подключение к БД) и до конца всех тестов живёт. Экономия — овердохуища.
  • scope='module' — раб на один файл с тестами.
  • scope='class' — раб на один класс. Для любителей ООП.
  • scope='function' (стоит по умолчанию) — самый работящий раб. Для КАЖДОГО теста заново всё готовит. Надёжно, но может быть долго, если он там ядро Linux компилирует.
  • autouse=True — это вообще инициативный хуй. Ты его даже не звал, а он уже всё приготовил и стоит с довольной рожей. Иногда полезно, но чаще — неожиданный пиздец.

Пример из жизни, чтобы не быть пиздаболом:

Допустим, тебе надо потестить что-то с пользователем в базе данных. Без фикстуры ты в каждом тесте пишешь INSERT, а потом забываешь DELETE и вся база превращается в помойку. С фикстурой — красота.

import pytest
import psycopg2

@pytest.fixture(scope="function")
def test_user(db_connection):
    """Фикстура создает тестового пользователя и удаляет его после теста."""
    cursor = db_connection.cursor()
    # Setup: создаем пользователя
    cursor.execute(
        "INSERT INTO users (username, email) VALUES (%s, %s) RETURNING id;",
        ("test_fixture_user", "test@example.com")
    )
    user_id = cursor.fetchone()[0]
    db_connection.commit()
    yield user_id  # Передаем ID в тест
    # Teardown: удаляем пользователя после теста
    cursor.execute("DELETE FROM users WHERE id = %s;", (user_id,))
    db_connection.commit()
    cursor.close()

def test_user_creation(test_user):
    """Тест использует фикстуру `test_user`, получая ID созданного пользователя."""
    # Здесь можно проверить, что пользователь доступен в системе
    assert test_user is not None
    # После выполнения теста фикстура автоматически выполнит teardown-часть (DELETE).

Смотри, какая магия: перед test_user_creation наш дворецкий test_user впендюривает в базу нового юзера и отдаёт тебе его id. Ты спокойно тестируешь. А как только тест кончился — неважно, упал он или прошёл — дворецкий возвращается и выполняет код ПОСЛЕ yield, то есть выпиливает этого юзера из базы. Чистота, блядь, идеальная! Никаких лишних следов.

Короче, фикстуры — это мощнейшая хуйня, чтобы не копипастить одно и то же и не тратить нервы на подготовку и уборку. Выучи — и жизнь станет проще, ёпта.