Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Что такое фикстуры в тестировании?
Фикстура (fixture) — это подготовленное состояние или окружение, которое используется при выполнении теста. Фикстура предоставляет данные, объекты или ресурсы, которые нужны тесту для работы. В Python фикстуры — это функции, которые запускаются перед тестом и готовят всё необходимое.
Зачем нужны фикстуры
- Изолированность — каждый тест работает с чистым состоянием
- Переиспользование — одну фикстуру можно использовать в разных тестах
- Читаемость — тест сосредоточен на логике, не на подготовке
- Простота — вместо дублирования кода подготовки
- Управление ресурсами — правильное открытие и закрытие ресурсов
Основной синтаксис с 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 проще и гибче!
Лучшие практики
- Переиспользование — одна фикстура для многих тестов
- Зависимости — ясно определяй, от каких фикстур зависит новая
- Имена — називай фикстуры по тому, что они предоставляют
- Очистка — всегда очищай ресурсы (БД, файлы, сокеты)
- Правильные scope — выбирай правильную область видимости
Вывод: Фикстуры в pytest — это мощный инструмент для написания чистых и надёжных тестов. Они снимают с разработчика рутину подготовки данных и позволяют сосредоточиться на логике тестирования.