Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Фикстура (Fixture)
Фикстура — это подготовленное состояние данных или окружение для тестирования. Фикстура предоставляет ресурсы, которые нужны тестам для выполнения (базовые данные, подключения к БД, моки объектов и т.д.). В контексте pytest фикстура — это функция, которая подготавливает данные перед тестом и может очищать ресурсы после теста.
Основная концепция
import pytest
# Простая фикстура
@pytest.fixture
def simple_data():
return {'name': 'John', 'age': 30}
# Тест использует фикстуру
def test_user_data(simple_data):
assert simple_data['name'] == 'John'
assert simple_data['age'] == 30
# Фикстура автоматически вызывается и передаётся в тест
Setup и Teardown
Фикстуры могут подготавливать ресурсы (setup) и очищать их (teardown):
import pytest
@pytest.fixture
def database():
# SETUP: подготовка
print('Подключение к БД')
db_connection = connect_to_database('localhost:5432')
db_connection.create_table('users')
# Передаём ресурс в тест
yield db_connection
# TEARDOWN: очистка
print('Закрытие соединения')
db_connection.close()
def test_database_operations(database):
# База данных готова
database.insert('users', {'name': 'Alice'})
assert database.count('users') == 1
# После теста автоматически вызовется teardown
Реальный пример: тестирование API
import pytest
from fastapi.testclient import TestClient
from app import create_app
# Фикстура для тестового приложения
@pytest.fixture
def test_app():
app = create_app()
app.config['TESTING'] = True
return app
# Фикстура для тестового клиента
@pytest.fixture
def client(test_app):
return TestClient(test_app)
# Фикстура для создания тестовых данных
@pytest.fixture
def test_user(client):
user_data = {'name': 'John', 'email': 'john@example.com'}
response = client.post('/users', json=user_data)
return response.json()
# Тест использует фикстуры
def test_get_user(client, test_user):
response = client.get(f'/users/{test_user["id"]}')
assert response.status_code == 200
assert response.json()['name'] == 'John'
def test_update_user(client, test_user):
update_data = {'name': 'Jane'}
response = client.put(f'/users/{test_user["id"]}', json=update_data)
assert response.status_code == 200
assert response.json()['name'] == 'Jane'
Фикстуры с параметризацией
import pytest
# Фикстура, которая возвращает разные значения
@pytest.fixture(params=[1, 5, 10, 100])
def list_size(request):
return request.param
# Тест будет запущен 4 раза с разными значениями
def test_list_creation(list_size):
my_list = list(range(list_size))
assert len(my_list) == list_size
# Результат:
# test_list_creation[1] PASSED
# test_list_creation[5] PASSED
# test_list_creation[10] PASSED
# test_list_creation[100] PASSED
Области видимости фикстур (Scopes)
import pytest
# function scope (по умолчанию) - для каждого теста
@pytest.fixture(scope='function')
def user_function_scope():
print('Creating user for test')
return {'id': 1, 'name': 'John'}
# class scope - общая для всех тестов в классе
@pytest.fixture(scope='class')
def database_class_scope():
print('Connecting to database')
yield database_connection
print('Closing database')
# module scope - общая для всех тестов в модуле
@pytest.fixture(scope='module')
def config_module_scope():
return load_config('test_config.yaml')
# session scope - одна на всю сессию тестирования
@pytest.fixture(scope='session')
def expensive_resource():
print('Initializing expensive resource once')
return expensive_initialization()
class TestDatabase:
def test_query1(self, database_class_scope):
# database_class_scope используется
pass
def test_query2(self, database_class_scope):
# Одно и то же подключение
pass
Зависимости между фикстурами
import pytest
# Фикстуры могут зависеть друг от друга
@pytest.fixture
def user():
return {'id': 1, 'name': 'Alice'}
@pytest.fixture
def post(user):
# Зависит от фикстуры user
return {
'id': 101,
'title': 'My Post',
'author_id': user['id']
}
@pytest.fixture
def comment(post, user):
# Зависит от двух фикстур
return {
'id': 1001,
'text': 'Great post!',
'post_id': post['id'],
'author_id': user['id']
}
def test_comment_structure(comment):
assert comment['post_id'] == 101
assert comment['author_id'] == 1
Практический пример: тестирование с БД
import pytest
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from models import Base, User
# Фикстура: тестовая БД
@pytest.fixture
def db_session():
# Создаём in-memory SQLite БД
engine = create_engine('sqlite:///:memory:')
Base.metadata.create_all(engine) # Создаём таблицы
Session = sessionmaker(bind=engine)
session = Session()
yield session
# Очистка
session.close()
# Фикстура: тестовый пользователь
@pytest.fixture
def test_user(db_session):
user = User(name='John', email='john@example.com')
db_session.add(user)
db_session.commit()
return user
# Тесты
def test_user_creation(db_session):
user = User(name='Alice', email='alice@example.com')
db_session.add(user)
db_session.commit()
found = db_session.query(User).filter_by(name='Alice').first()
assert found.email == 'alice@example.com'
def test_user_retrieval(db_session, test_user):
found = db_session.query(User).filter_by(id=test_user.id).first()
assert found.name == 'John'
def test_user_update(db_session, test_user):
test_user.name = 'Jane'
db_session.commit()
found = db_session.query(User).filter_by(id=test_user.id).first()
assert found.name == 'Jane'
Фикстуры с autouse
import pytest
# Фикстура автоматически используется для всех тестов
@pytest.fixture(autouse=True)
def reset_global_state():
# Подготовка
global_var = 0
yield
# Очистка - выполняется после каждого теста
print('Resetting global state')
def test_something():
# reset_global_state вызовется автоматически
assert True
def test_another():
# reset_global_state вызовется и здесь
assert True
Конфигурация фикстур в conftest.py
# conftest.py - специальный файл pytest
# Фикстуры здесь доступны для всех тестов в проекте
import pytest
import tempfile
import os
@pytest.fixture(scope='session')
def test_config():
return {
'database_url': 'sqlite:///:memory:',
'api_url': 'http://localhost:8000',
'debug': True
}
@pytest.fixture
def temp_dir():
# Создаём временную директорию
with tempfile.TemporaryDirectory() as tmpdir:
yield tmpdir
# Автоматически удаляется после теста
@pytest.fixture
def json_file(temp_dir):
filepath = os.path.join(temp_dir, 'test.json')
with open(filepath, 'w') as f:
f.write('{"key": "value"}')
yield filepath
Сравнение: старый setup/teardown vs новые фикстуры
# СТАРЫЙ СПОСОБ (unittest)
class TestExample(unittest.TestCase):
def setUp(self):
# Выполняется перед каждым тестом
self.data = [1, 2, 3]
def tearDown(self):
# Выполняется после каждого теста
self.data = None
def test_something(self):
assert len(self.data) == 3
# НОВЫЙ СПОСОБ (pytest fixtures)
@pytest.fixture
def data():
# Setup
data = [1, 2, 3]
yield data
# Teardown (опционально)
def test_something(data):
assert len(data) == 3
# Преимущества фикстур:
# 1. Более читаемо
# 2. Зависимости явные
# 3. Можно использовать разные области видимости
# 4. Параметризация легче
Фикстуры для моков
import pytest
from unittest.mock import Mock, patch
# Фикстура с mock объектом
@pytest.fixture
def mock_database():
mock = Mock()
mock.query.return_value = [{'id': 1, 'name': 'John'}]
return mock
@pytest.fixture
def mock_api():
with patch('requests.get') as mock_get:
mock_get.return_value.json.return_value = {'status': 'ok'}
yield mock_get
def test_with_mock_db(mock_database):
result = mock_database.query()
assert len(result) == 1
def test_with_mock_api(mock_api):
# API мок используется в этом тесте
assert True
Лучшие практики
import pytest
# ХОРОШО: понятное имя, одна ответственность
@pytest.fixture
def authenticated_user():
user = User(name='John', is_authenticated=True)
return user
# ХОРОШО: явное описание через docstring
@pytest.fixture
def database():
"""Инициализирует in-memory SQLite для тестов"""
engine = create_engine('sqlite:///:memory:')
yield engine
engine.dispose()
# ПЛОХО: фикстура делает слишком много
@pytest.fixture
def everything():
db = create_database()
api = init_api()
config = load_config()
return (db, api, config)
# ХОРОШО: разные фикстуры для разных вещей
@pytest.fixture
def database():
return create_database()
@pytest.fixture
def api():
return init_api()
@pytest.fixture
def config():
return load_config()
Вывод
Фикстура — это ключевой механизм в pytest для подготовки тестового окружения:
- Инициализирует данные и ресурсы перед тестом
- Очищает ресурсы после теста (yield pattern)
- Может зависеть от других фикстур
- Поддерживает разные области видимости
- Делает тесты чище, читабельнее и переиспользуемыми
Фикстуры — это одна из самых мощных и полезных фич pytest, которые позволяют писать чистые и поддерживаемые тесты.