Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Когда вызывается фикстура в Pytest?
Фикстуры (fixtures) в Pytest — это мощный механизм для предоставления данных, состояния или ресурсов тестам. Их вызов зависит от области видимости (scope) и способа их объявления в тестовых функциях или классах. Понимание времени вызова критически важно для эффективного использования фикстур, управления зависимостями и оптимизации времени выполнения тестов.
Ключевые факторы, определяющие момент вызова
Время вызова фикстуры определяется двумя основными параметрами:
- Область видимости (
scope): Определяет, как часто фикстура создается и разрушается. - Запрос фикстуры тестом: Фикстура вызывается только тогда, когда тестовая функция явно запрашивает её в качестве аргумента.
Детальный разбор по областям видимости
1. scope="function" (значение по умолчанию)
Фикстура вызывается один раз для каждой тестовой функции, которая её запрашивает.
- Когда вызывается: Непосредственно перед выполнением каждого теста, который имеет параметр с именем фикстуры.
- Когда завершается: Сразу после завершения этого теста (если используется
yieldилиaddfinalizer). - Аналог:
@BeforeEach/@AfterEachв JUnit.
import pytest
@pytest.fixture(scope="function")
def fresh_database():
print("\nИнициализация БД для теста...")
db = {"users": []}
yield db # Передача данных тесту
print("\nОчистка БД после теста...")
def test_add_user(fresh_database):
fresh_database["users"].append("Alice")
assert len(fresh_database["users"]) == 1
def test_count_users(fresh_database):
# Эта функция получит СВОЙ, новый экземпляр `fresh_database`
assert len(fresh_database["users"]) == 0
Вывод:
Инициализация БД для теста...
Очистка БД после теста...
.Инициализация БД для теста...
Очистка БД после теста...
.
2. scope="class"
Фикстура вызывается один раз для всего тестового класса, независимо от количества методов в нём.
- Когда вызывается: Один раз перед выполнением первого тестового метода в классе, который запрашивает фикстуру.
- Когда завершается: После выполнения последнего тестового метода в этом классе.
import pytest
@pytest.fixture(scope="class")
def shared_config():
print("\n>>> Загрузка конфигурации для класса")
config = {"mode": "test", "timeout": 30}
yield config
print("\n<<< Выгрузка конфигурации для класса")
@pytest.mark.usefixtures("shared_config")
class TestAPI:
def test_endpoint_a(self, shared_config):
assert shared_config["mode"] == "test"
def test_endpoint_b(self, shared_config):
# Оба метода используют один и тот же объект `shared_config`
assert shared_config["timeout"] == 30
Вывод: Фикстура shared_config будет инициализирована и завершена только один раз для всего TestAPI.
3. scope="module"
Фикстура вызывается один раз для всего модуля (файла с тестами).
- Когда вызывается: Один раз при первом запросе фикстуры любым тестом в этом модуле.
- Когда завершается: После выполнения всех тестов в модуле.
# test_module.py
import pytest
import expensive_calculations
@pytest.fixture(scope="module")
def heavy_computation_result():
print("\n--- Выполнение дорогостоящих вычислений для модуля ---")
result = expensive_calculations.compute() # Долгая операция
yield result
print("\n--- Освобождение ресурсов модуля ---")
def test_result_type(heavy_computation_result):
assert isinstance(heavy_computation_result, dict)
def test_result_value(heavy_computation_result):
# Оба теста используют результат одного вычисления
assert heavy_computation_result["status"] == "ok"
4. scope="session"
Фикстура вызывается один раз за всю сессию выполнения тестов (т.е. за один запуск pytest).
- Когда вызывается: Один раз в самом начале тестовой сессии, при первом запросе этой фикстуры любым тестом.
- Когда завершается: В самом конце тестовой сессии, после выполнения всех тестов во всех модулях.
- Типичное использование: Установка соединения с базой данных, запуск и остановка Selenium-сервера или веб-приложения для тестирования.
# conftest.py
import pytest
@pytest.fixture(scope="session")
def browser():
from selenium import webdriver
print("\n===== Запуск браузера для сессии =====")
driver = webdriver.Chrome()
driver.implicitly_wait(10)
yield driver
driver.quit()
print("\n===== Закрытие браузера после сессии =====")
Все тесты во всех модулях проекта, которые запрашивают фикстуру browser, будут использовать один и тот же экземпляр браузера.
Важные особенности вызова
- Автоматическое использование (
autouse=True): Фикстура с этим параметром будет вызываться автоматически для всех тестов в её области видимости, без явного запроса в аргументах теста. Это влияет только на момент начала использования, но не отменяет правила области видимости. - Зависимости фикстур: Если фикстура
Aзависит от фикстурыB(указана в её параметрах), тоBбудет вызвана непосредственно перед вызовомA, с учётом своей области видимости. - Финальная логика (
yieldvsaddfinalizer): Код послеyieldили зарегистрированный черезrequest.addfinalizer()выполняется в момент завершения соответствующей области видимости (после теста, класса, модуля или сессии). Это гарантирует корректное освобождение ресурсов.
Практические рекомендации
- Используйте
scope="session"для дорогостоящих, но неизменяемых в течение прогона операций (логин, запуск сервиса). - Используйте
scope="module"для подготовки данных, общих для всех тестов в одном файле. scope="function"— ваш выбор по умолчанию для изоляции тестов друг от друга.- Старайтесь избегать
autouse=True, если только фикстура не нужна абсолютно всем тестам в области. Явные зависимости улучшают читаемость.
Таким образом, момент вызова фикстуры — это управляемый компромисс между производительностью (меньше вызовов) и изоляцией тестов (больше вызовов, но чище окружение). Правильный выбор scope является ключевым навыком при проектировании тестового фреймворка.