Фикстуры

Что такое фикстура?

подробнее

Фикстуры в pytest — это вспомогательные функции для подготовки среды и очистки ресурсов. Декоратор @pytest.fixture помечает функцию как ресурс. Тест получает результат фикстуры, принимая её имя как аргумент.

Пример простейшей фикстуры

@pytest.fixture
def my_fixture():
    return "hello world"

def test_helloworld(my_fixture):
    print(my_fixture)  # hello world

Какие есть встроенные фикстуры?

подробнее
  • tmp_path: Создание временной директории. Возвращает объект pathlib.Path. Директория уникальна для каждого теста и автоматически удаляется через несколько запусков.
    Пример:

    f = tmp_path / "test.txt"
    f.write_text("hello")
    
  • monkeypatch: Подмена (mock) атрибутов, словарей, os.environ. Незаменима, когда нужно временно “сломать” или заменить поведение функции, чтобы не ходить в реальную сеть или БД.
    Пример: monkeypatch.setenv("DATABASE_URL", "mock_url")
  • capsys: Перехват вывода в консоль (stdout, stderr). Например можно перехватить print(...) и проверить что выведется то, что нужно.
    Пример: out, err = capsys.readouterr()
  • request: “Магическая” фикстура, которая дает доступ к контексту выполнения теста. Часто используется внутри других фикстур для получения параметров.
    Пример: request.node.name или request.param
  • pytestconfig: Доступ к конфигам (pytest.ini) и флагам CLI (-v, -s, --lf, …).
    Пример: pytestconfig.getoption("verbose")

Какие параметры принимает декоратор @pytest.fixture?

подробнее
  • scope — область действия (жизненный цикл) фикстуры. (function, class, module, package, session)
  • params — список значений для параметризации фикстуры. Тест запустится столько раз, сколько элементов в списке. Доступ к значениям внутри фикстуры — через request.param.
  • autouse (bool) — если True, фикстура применится ко всем тестам в её области видимости автоматически, даже если её не указывать в аргументах теста.
  • ids — список строк или функция для формирования красивых названий тестов при параметризации.
  • name — позволяет задать фикстуре псевдоним. В тесте тогда нужно использовать это имя, а не имя самой функции.

Пример

@pytest.fixture(scope="session", autouse=True, params=[1, 2], ids=["one", "two"], name="my_db")
def db_fixture(request):
    return request.param


def test_db(my_db):
    ...

Какие бывают области видимости (scope) у фикстур?

подробнее

Параметр scope можно понимать как область видимости, область действия, жизненный цикл или уровень. Он определяет «живучесть» фикстуры (как часто она пересоздаётся).

  • function (по умолчанию) — создаётся для каждого теста (каждой функции).
  • class — один раз на класс тестов.
  • module — один раз на модуль.
  • package — один раз на пакет.
  • session — один раз на весь прогон тестов.

Параметризация фикстур

подробнее

Параметризация фикстур позволяет запустить один и тот же набор тестов несколько раз с разными входными данными. Это «умножает» количество тестов без дублирования кода.
Параметризация фикстуры удобна, если одни и те же данные (например, разные БД или браузеры) нужны множеству разных тестов в отличии от @pytest.mark.parametrize, когда когда данные нужны только этому тесту.

Для параметризации используются:

  • params: Список значений.
  • request: Специальная фикстура, через которую мы получаем текущее значение из params.
  • ids: Список имен для каждого прогона (чтобы в консоли тесты назывались понятно).

Пример

@pytest.fixture(params=["chrome", "firefox", "safari"], ids=["CH", "FF", "SF"])
def browser(request):
    driver = setup_browser(request.param) # request.param — текущее значение
    yield driver
    driver.quit()

def test_login(browser):
    browser.get("https://site.com")
    # Этот тест выполнится 3 раза (для каждого браузера)

Можно ли передать фикстуру в фикстуру?

подробнее

Фикстура может принимать другие фикстуры как аргументы.
Pytest сначала выполнит «вложенную» фикстуру, а затем ту, которая её вызывает.
Области видимости (Scopes): Важно помнить правило: фикстура с широким scope (например, session) не может зависеть от фикстуры с узким scope (например, function). Наоборот — можно.

Пример

@pytest.fixture
def user_data():
    return {"name": "Alice", "role": "admin"}

@pytest.fixture
def user_token(user_data): # Передаем фикстуру в фикстуру
    return f"token_{user_data['name']}"

def test_api(user_token):
    assert "Alice" in user_token

Преимущества

  • Атомарность: Каждая фикстура делает что-то одно (одна создает юзера, другая — авторизует).
  • Переиспользование: Не нужно в каждой фикстуре заново писать код создания пользователя.

yield в фикстуре

подробнее

return завершает выполнение функции. Код после него не выполнится.
yield “ставит функцию на паузу”, пока идет тест, а потом возвращается, чтобы доделать очистку.

Ключевое слово yield разделяет работу фикстуры на два этапа:

  • Setup (Подготовка): Весь код до yield. Выполняется перед тестом.
  • Teardown (Очистка): Весь код под yield. Выполняется сразу после завершения теста (даже если тест упал с ошибкой).

То, что стоит после yield, передается в тест как аргумент.

Пример

@pytest.fixture
def db():
    # --- Setup ---
    connection = connect_db()
    
    yield connection  # Передаем ресурс тесту

    # --- Teardown ---
    connection.close()

Параметр autouse

подробнее

Чтобы фикстура запускалась автоматически для всех тестов в её области видимости (без передачи её в аргументы), используется параметр autouse=True.
Зачем: Для фоновых действий: логирование, замер времени, очистка БД перед каждым тестом, создание папок.
Опасность: Чрезмерное использование делает тесты “магическими” и менее понятными.

Пример

@pytest.fixture(autouse=True)
def clean_logs():
    clear_log_file() # Будет запускаться перед КАЖДЫМ тестом