Ответ
Фикстуры (fixtures) в pytest
— это специальные функции, предназначенные для подготовки тестового окружения, предоставления данных или ресурсов для тестов, а также для выполнения очистки (teardown) после их завершения. Они являются центральным элементом архитектуры pytest
для создания модульных, переиспользуемых и легко поддерживаемых тестов.
Почему фикстуры предпочтительнее?
Фикстуры решают проблемы, присущие традиционным методам setup/teardown (например, setUp
/tearDown
в unittest
):
- Явные зависимости: Тесты явно объявляют, какие фикстуры им нужны, просто указывая их как аргументы функции. Это делает тесты более читаемыми и понятными.
- Переиспользование: Фикстуры могут быть легко переиспользованы множеством тестов и даже в других фикстурах, что значительно сокращает дублирование кода.
- Модульность и инверсия управления:
pytest
сам управляет жизненным циклом фикстур, создавая их по требованию и уничтожая после использования, что упрощает написание тестов. - Гибкость: Поддерживают различные области видимости (scope) и параметризацию.
Основные особенности:
- Объявление: Фикстуры объявляются с помощью декоратора
@pytest.fixture
. - Инъекция: Они передаются в тестовые функции или другие фикстуры как обычные аргументы.
- Область видимости (Scope): Определяет, как часто фикстура будет создаваться и уничтожаться:
function
(по умолчанию): Выполняется один раз для каждого тестового вызова.class
: Выполняется один раз для каждого тестового класса.module
: Выполняется один раз для каждого модуля.session
: Выполняется один раз за всю тестовую сессию.
- Setup и Teardown: Для выполнения операций очистки используется ключевое слово
yield
. Код доyield
выполняется как setup, а код послеyield
— как teardown. Еслиyield
не используется, фикстура просто возвращает значение.
Пример:
import pytest
# Фикстура, предоставляющая объект пользователя
@pytest.fixture(scope="function")
def user_data():
"""Фикстура, создающая данные пользователя для каждого теста."""
print("n[SETUP] Создание пользователя...")
user = {"id": 1, "name": "Alice", "email": "alice@example.com"}
yield user # Передача данных в тест
print(f"[TEARDOWN] Удаление пользователя {user['name']}...")
# Фикстура, зависящая от user_data
@pytest.fixture(scope="session")
def db_connection():
"""Фикстура, имитирующая подключение к БД, создаётся один раз за сессию."""
print("n[SETUP] Установка соединения с БД...")
conn = "Database Connection Object"
yield conn
print("[TEARDOWN] Закрытие соединения с БД.")
def test_user_name(user_data, db_connection):
"""Тест, использующий фикстуры user_data и db_connection."""
print(f"Тест: Проверка имени пользователя. Используется БД: {db_connection}")
assert user_data["name"] == "Alice"
def test_user_email(user_data):
"""Другой тест, использующий только user_data."""
print("Тест: Проверка email пользователя.")
assert "example.com" in user_data["email"]
# Для запуска: pytest -s your_test_file.py
Дополнительные возможности:
- Параметризация: Фикстуры могут быть параметризованы с помощью
@pytest.mark.parametrize
илиpytest.fixture(params=...)
, что позволяет запускать один и тот же тест с разными наборами входных данных. - Автоматическое использование: Фикстуры могут быть автоматически применены ко всем тестам в определённой области видимости с помощью
autouse=True
. - Переопределение: Фикстуры могут быть переопределены в более узких областях видимости, что полезно для настройки поведения тестов.