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

Что такое фикстуры в тестировании?

2.0 Middle🔥 191 комментариев
#Тестирование

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что такое фикстуры в тестировании?

Фикстура (fixture) — это подготовленное состояние или окружение, которое используется при выполнении теста. Фикстура предоставляет данные, объекты или ресурсы, которые нужны тесту для работы. В Python фикстуры — это функции, которые запускаются перед тестом и готовят всё необходимое.

Зачем нужны фикстуры

  1. Изолированность — каждый тест работает с чистым состоянием
  2. Переиспользование — одну фикстуру можно использовать в разных тестах
  3. Читаемость — тест сосредоточен на логике, не на подготовке
  4. Простота — вместо дублирования кода подготовки
  5. Управление ресурсами — правильное открытие и закрытие ресурсов

Основной синтаксис с pytest

import pytest

# ПРОСТАЯ ФИКСТУРА
@pytest.fixture
def user_data():
    """Подготавливает тестовые данные пользователя"""
    return {
        "id": 1,
        "email": "test@example.com",
        "username": "testuser",
        "password": "secure_password"
    }

# ИСПОЛЬЗОВАНИЕ В ТЕСТЕ
def test_user_creation(user_data):
    """user_data передаётся как параметр"""
    user = User(**user_data)
    assert user.email == "test@example.com"
    assert user.username == "testuser"

Фикстуры с setUp/tearDown

# С подготовкой и очисткой (setup и teardown)
@pytest.fixture
def database_connection():
    """Создаёт подключение и закрывает его после теста"""
    # SETUP (подготовка)
    db = Database("sqlite:///:memory:")
    db.connect()
    
    # Код теста выполняется здесь
    yield db
    
    # TEARDOWN (очистка)
    db.close()

def test_insert_user(database_connection):
    db = database_connection
    user = db.insert("users", {"name": "John"})
    assert user.id is not None
    # После теста автоматически вызывается db.close()

Области видимости фикстур (Scope)

import pytest

# 1. FUNCTION (по умолчанию) — создаётся для каждого теста
@pytest.fixture(scope="function")
def user():
    print("\nСоздание пользователя для теста")
    return {"id": 1, "name": "John"}

# 2. CLASS — создаётся один раз для класса тестов
@pytest.fixture(scope="class")
def expensive_resource():
    print("\nПодготовка дорогого ресурса")
    resource = ExpensiveResource()
    resource.initialize()
    yield resource
    resource.cleanup()

# 3. MODULE — создаётся один раз на весь модуль
@pytest.fixture(scope="module")
def database():
    print("\nПодключение к БД (один раз для модуля)")
    db = Database()
    db.connect()
    yield db
    db.disconnect()

# 4. SESSION — создаётся один раз на всю сессию тестирования
@pytest.fixture(scope="session")
def config():
    print("\nЗагрузка конфигурации")
    return load_config("config.yaml")

# ПРИМЕР ИСПОЛЬЗОВАНИЯ
class TestUserOperations:
    def test_create_user(self, user):
        assert user["name"] == "John"
    
    def test_update_user(self, user):
        assert user["id"] == 1

Параметризованные фикстуры

import pytest

# Фикстура, которая возвращает разные значения
@pytest.fixture(params=["admin", "user", "guest"])
def user_role(request):
    """Возвращает разные роли пользователей"""
    return request.param

def test_permissions(user_role):
    """Тест выполнится 3 раза: с admin, user, guest"""
    user = User(role=user_role)
    assert user.role in ["admin", "user", "guest"]

# Результат: тест_permissions выполнится 3 раза
# test_permissions[admin]
# test_permissions[user]
# test_permissions[guest]

Фикстуры с зависимостями

import pytest

@pytest.fixture
def user_service():
    """Сервис для работы с пользователями"""
    return UserService()

@pytest.fixture
def email_service():
    """Сервис для отправки email"""
    return EmailService()

# Фикстура, зависящая от других фикстур
@pytest.fixture
def notification_system(user_service, email_service):
    """Создаёт систему уведомлений на основе других сервисов"""
    notifier = NotificationSystem()
    notifier.attach_user_service(user_service)
    notifier.attach_email_service(email_service)
    return notifier

def test_notification(notification_system):
    """Использует фикстуру, которая зависит от других"""
    notification_system.notify("user@example.com", "Hello!")

Практический пример с БД

import pytest
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, Session

# ФИКСТУРА БД
@pytest.fixture(scope="session")
def db_engine():
    """Создаёт тестовую БД на всю сессию"""
    engine = create_engine("sqlite:///:memory:")
    Base.metadata.create_all(engine)  # Создаём таблицы
    yield engine
    Base.metadata.drop_all(engine)  # Удаляем таблицы

# ФИКСТУРА СЕССИИ
@pytest.fixture
def db_session(db_engine):
    """Создаёт сессию для каждого теста и откатывает изменения"""
    connection = db_engine.connect()
    transaction = connection.begin()
    session = sessionmaker(bind=connection)()
    
    yield session
    
    session.close()
    transaction.rollback()  # Откатываем все изменения
    connection.close()

# ФИКСТУРА ПОЛЬЗОВАТЕЛЯ
@pytest.fixture
def user_in_db(db_session):
    """Создаёт пользователя в тестовой БД"""
    user = User(
        email="test@example.com",
        username="testuser",
        password="hashed_password"
    )
    db_session.add(user)
    db_session.commit()
    return user

# ИСПОЛЬЗОВАНИЕ В ТЕСТАХ
def test_user_exists(db_session, user_in_db):
    """Проверяет, что пользователь сохранён в БД"""
    found_user = db_session.query(User).filter_by(
        username="testuser"
    ).first()
    assert found_user is not None
    assert found_user.email == "test@example.com"

def test_user_isolation(db_session, user_in_db):
    """Каждый тест работает с чистым состоянием"""
    # user_in_db из одного теста не влияет на другой
    count = db_session.query(User).count()
    assert count == 1  # Только пользователь из фикстуры

Mock объекты как фикстуры

import pytest
from unittest.mock import Mock, patch

# MOCK ФИКСТУРА
@pytest.fixture
def mock_email_service():
    """Создаёт mock сервиса отправки email"""
    mock = Mock()
    mock.send.return_value = True
    return mock

def test_user_registration(mock_email_service):
    """Тест с mock сервисом"""
    user_service = UserService(email_service=mock_email_service)
    user_service.register("test@example.com", "password")
    
    # Проверяем, что email был отправлен
    mock_email_service.send.assert_called_once_with(
        "test@example.com",
        "Welcome!"
    )

# ФИКСТУРА С PATCH
@pytest.fixture
def mock_requests():
    """Mock для requests библиотеки"""
    with patch('requests.get') as mock_get:
        mock_get.return_value.status_code = 200
        mock_get.return_value.json.return_value = {"data": "test"}
        yield mock_get

def test_api_call(mock_requests):
    """Тест с mock HTTP запроса"""
    response = requests.get("https://api.example.com/users")
    assert response.status_code == 200
    mock_requests.assert_called_with("https://api.example.com/users")

Автоиспользование фикстур (autouse)

import pytest

@pytest.fixture(autouse=True)
def log_test_info():
    """Фикстура выполняется автоматически для всех тестов"""
    print("\n--- НАЧАЛО ТЕСТА ---")
    yield
    print("--- КОНЕЦ ТЕСТА ---")

def test_something():
    """Даже без явного указания фикстуры, она выполнится"""
    assert True
    # Вывод:
    # --- НАЧАЛО ТЕСТА ---
    # --- КОНЕЦ ТЕСТА ---

Сравнение: unittest vs pytest

unittest (старый стиль)

import unittest

class TestUser(unittest.TestCase):
    def setUp(self):  # Выполняется перед каждым тестом
        self.user = User(name="John")
    
    def tearDown(self):  # Выполняется после каждого теста
        self.user = None
    
    def test_user_name(self):
        assert self.user.name == "John"

pytest (новый стиль)

import pytest

@pytest.fixture
def user():
    return User(name="John")

def test_user_name(user):
    assert user.name == "John"

pytest проще и гибче!

Лучшие практики

  1. Переиспользование — одна фикстура для многих тестов
  2. Зависимости — ясно определяй, от каких фикстур зависит новая
  3. Имена — називай фикстуры по тому, что они предоставляют
  4. Очистка — всегда очищай ресурсы (БД, файлы, сокеты)
  5. Правильные scope — выбирай правильную область видимости

Вывод: Фикстуры в pytest — это мощный инструмент для написания чистых и надёжных тестов. Они снимают с разработчика рутину подготовки данных и позволяют сосредоточиться на логике тестирования.

Что такое фикстуры в тестировании? | PrepBro