Pytest.fixture: примеры (PYTHON)
pytest.fixture(scope, params, autouse, ids, name): functionОсновные сведения о pytest.fixture
Декоратор @pytest.fixture используется в тестовом фреймворке pytest для создания фикстур. Фикстуры представляют собой функции, которые подготавливают данные, состояние или ресурсы для тестов и выполняют очистку после их завершения. Их применение позволяет избежать дублирования кода и повышает читаемость тестов.
Фикстуры используются, когда нескольким тестам требуются одинаковые подготовительные действия, такие как создание объектов, подключение к базам данных, настройка временных файлов или мокирование внешних сервисов.
Аргументы декоратора pytest.fixture
- scope: определяет время жизни фикстуры. Допустимые значения:
'function'(по умолчанию, выполняется для каждого теста),'class'(для каждого класса),'module'(для каждого модуля),'package'(для каждого пакета),'session'(один раз за сессию). - params: необязательный итерируемый объект для параметризации фикстуры. Каждое значение вызывает новый экземпляр теста, зависящий от фикстуры.
- autouse: булево значение (по умолчанию
False). ЕслиTrue, фикстура активируется автоматически для всех тестов в её области видимости без явного объявления в аргументах теста. - ids: список строковых идентификаторов для параметризованных фикстур. Упрощает идентификацию каждого набора параметров.
- name: строка, задающая альтернативное имя для фикстуры. По умолчанию используется имя функции.
Фикстура возвращает значение, которое передается в тестовую функцию или другую фикстуру в качестве аргумента. При использовании yield код после него выполняется как завершающая часть (teardown).
Простые примеры применения
Пример фикстуры с областью видимости на уровне функции, которая возвращает данные.
import pytest
@pytest.fixture
def sample_data():
return {'key': 'value', 'number': 42}
def test_data(sample_data):
assert sample_data['number'] == 42Тест проходит успешно.
Фикстура с параметризацией через аргумент params.
@pytest.fixture(params=[1, 2, 3])
def parametrized_fixture(request):
return request.param
def test_parametrized(parametrized_fixture):
assert parametrized_fixture > 0Тест выполняется три раза для каждого значения параметра.
Автоматически используемая фикстура с autouse=True.
@pytest.fixture(autouse=True)
def auto_fixture():
print('\nФикстура запущена автоматически')
yield
print('Очистка после теста')
def test_autouse():
assert TrueФикстура запущена автоматически Очистка после теста Тест проходит.
Изменение имени фикстуры с помощью аргумента name.
@pytest.fixture(name='custom_name')
def original_name():
return 'данные'
def test_with_name(custom_name):
assert custom_name == 'данные'Тест проходит, фикстура доступна под именем custom_name.
Похожие инструменты в Python
В стандартном модуле unittest используются методы setUp и tearDown (а также setUpClass, setUpModule). Они привязаны к классам или модулям и менее гибкие, чем фикстуры pytest, но не требуют установки дополнительных библиотек.
Контекстные менеджеры (использование with) также могут подготавливать и освобождать ресурсы. Они удобны для управления ресурсами с четкими границами жизни, но не интегрированы напрямую в тестовый фреймворк.
Фабричные функции без декораторов просто возвращают данные, но не обеспечивают автоматическую очистку и сложнее в поддержке при зависимостях.
Фикстуры pytest предпочтительнее для комплексных тестовых наборов из-за гибкости областей видимости, параметризации и автоматического управления зависимостями.
Аналоги в других языках программирования
В JavaScript (Jest) используются хуки, такие как beforeEach, afterEach.
// Jest пример
beforeEach(() => {
initializeData();
});Функция выполняется перед каждым тестом.
В Java (JUnit 5) применяются аннотации @BeforeEach, @AfterEach. Параметризация осуществляется через @ParameterizedTest.
@BeforeEach
void setUp() {
data = new ArrayList<>();
}В PHP (PHPUnit) существуют методы setUp и tearDown.
protected function setUp(): void {
$this->stack = [];
}В C# (NUnit, xUnit) используются атрибуты, такие как [SetUp] и [TearDown]. xUnit применяет конструктор и IDisposable для настройки и очистки.
[SetUp]
public void Setup() {
_service = new MyService();
}В Go (стандартная библиотека testing) настройка часто выполняется в начале тестовой функции, а очистка через defer. Отсутствуют встроенные фикстуры, но есть сторонние решения.
func TestExample(t *testing.T) {
db := setupDB()
defer db.Close()
// тест
}В Kotlin (KotlinTest) поддерживаются фикстуры, похожие на pytest, с помощью интерфейсов, таких как BeforeTest и AfterTest.
override fun beforeTest() {
println("Настройка")
}Основное отличие pytest.fixture - декларативный подход с использованием декораторов и автоматическое разрешение зависимостей, что часто делает код компактнее.
Распространенные ошибки
Циклические зависимости между фикстурами приводят к ошибке.
import pytest
@pytest.fixture
def fixture_a(fixture_b):
return 1
@pytest.fixture
def fixture_b(fixture_a):
return 2pytest.fixture.FixtureLookupError: циклическая зависимость обнаружена
Использование неподдерживаемого значения для scope вызывает исключение.
@pytest.fixture(scope='invalid')
def bad_fixture():
return NoneValueError: несмотря на объявление области видимости 'invalid', фикстура может быть вызвана только на уровне 'function'
Передача параметра напрямую в фикстуру без использования request.param.
@pytest.fixture(params=[1,2])
def fix(param): # Ошибка: param не определен
return paramTypeError: фикстура() принимает 1 позиционный аргумент, но задано 0
Попытка использовать фикстуру вне тестового контекста, например, в обычной функции.
fix = sample_data() # sample_data - фикстура
print(fix)Ошибка: фикстура 'sample_data' не может быть вызвана напрямую. Фикстуры предназначены для тестов.
Изменения в новых версиях
В pytest версии 5.0 появилась поддержка асинхронных фикстур. Для их создания используется @pytest.fixture с асинхронными функциями.
import pytest
@pytest.fixture
async def async_data():
return await fetch_data()
@pytest.mark.asyncio
async def test_async(async_data):
assert async_data == 'result'Начиная с версии 6.2, улучшена обработка ошибок в фикстурах, включая более информативные сообщения при неудачах в фазах setup/teardown.
В версии 7.0 добавлена возможность использовать фикстуры в хуках, таких как pytest_fixture_setup, для более глубокой интеграции.
Параметр scope='package' стал стабильным и рекомендуется для использования.
Расширенные примеры применения
Фикстура с использованием yield для разделения кода настройки и очистки.
@pytest.fixture
def resource():
print('Настройка ресурса')
data = ['важные', 'данные']
yield data
print('Очистка ресурса')
def test_resource(resource):
assert 'данные' in resourceНастройка ресурса Очистка ресурса Тест проходит.
Фикстура, зависящая от другой фикстуры, демонстрирующая автоматическое разрешение зависимостей.
@pytest.fixture
def db_connection():
return 'подключение к БД'
@pytest.fixture
def user(db_connection):
return {'name': 'Алексей', 'db': db_connection}
def test_user(user):
assert user['db'] == 'подключение к БД'Тест проходит, фикстура user получает db_connection автоматически.
Параметризованная фикстура с использованием ids для понятных имен тестов.
@pytest.fixture(params=[
(1, 2, 3),
(5, 5, 10)
], ids=['сложение 1+2', 'сложение 5+5'])
def numbers(request):
return request.param
def test_sum(numbers):
a, b, expected = numbers
assert a + b == expectedТесты выполняются с указанными идентификаторами в отчете.
Фикстура с областью видимости на уровне модуля, которая кэширует данные.
@pytest.fixture(scope='module')
def cached_data():
print('Вычисление данных для модуля')
return complex_calculation() # предположим, функция существует
def test_one(cached_data):
assert cached_data is not None
def test_two(cached_data):
assert cached_data == cached_data'Вычисление данных для модуля' выводится один раз, данные используются в обоих тестах.
Использование фикстуры с фабрикой для создания динамических данных.
@pytest.fixture
def make_item():
created_items = []
def _make(name):
item = {'id': len(created_items), 'name': name}
created_items.append(item)
return item
yield _make
print(f'Создано элементов: {len(created_items)}')
def test_factory(make_item):
item1 = make_item('первый')
item2 = make_item('второй')
assert item1['id'] != item2['id']Создано элементов: 2 Тест проходит.
Асинхронная фикстура с использованием asyncio.
import asyncio
import pytest
@pytest.fixture
async def async_fixture():
await asyncio.sleep(0.1)
return 'асинхронный результат'
@pytest.mark.asyncio
async def test_async_fixture(async_fixture):
result = await async_fixture
assert 'асинхронный' in resultТест проходит после короткой задержки.