Spec-Zone.ru › pytest

Справочник по фикстурам

См. также

О фикстурах

См. также

Как использовать фикстуры

Встроенные фикстуры

Фикстуры определяются с помощью декоратора @pytest.fixture. В pytest есть несколько полезных встроенных фикстур:

capfd

Перехватывает вывод в текстовом виде в файловые дескрипторы 1 и 2.

capfdbinary

Перехватывает вывод в виде байтов в файловые дескрипторы 1 и 2.

caplog

Управляет журналированием и предоставляет доступ к записям журнала.

capsys

Перехватывает вывод в текстовом виде в sys.stdout и sys.stderr.

capteesys

Перехватывает вывод так же, как capsys, но также передаёт текст дальше в соответствии с --capture.

capsysbinary

Перехватывает вывод в виде байтов в sys.stdout и sys.stderr.

cache

Сохраняет значения между запусками pytest и извлекает их.

doctest_namespace

Предоставляет словарь, добавляемый в пространство имён doctest.

monkeypatch

Временно изменяет классы, функции, словари, os.environ и другие объекты.

pytestconfig

Предоставляет доступ к значениям конфигурации, менеджеру плагинов и хукам плагинов.

subtests

Позволяет объявлять под-тесты внутри тестовых функций.

record_property

Добавляет дополнительные свойства к тесту.

record_testsuite_property

Добавляет дополнительные свойства к набору тестов.

recwarn

Записывает предупреждения, выданные тестовыми функциями.

request

Предоставляет информацию о выполняемой тестовой функции.

testdir

Предоставляет временный каталог для запуска и тестирования плагинов pytest.

tmp_path

Предоставляет объект pathlib.Path, указывающий на временный каталог, уникальный для каждой тестовой функции.

tmp_path_factory

Создаёт временные каталоги на время сессии и возвращает объекты pathlib.Path.

tmpdir

Предоставляет объект py.path.local, указывающий на временный каталог, уникальный для каждой тестовой функции; заменён на tmp_path.

tmpdir_factory

Создаёт временные каталоги на время сессии и возвращает объекты py.path.local; заменён на tmp_path_factory.

Доступность фикстур

Доступность фикстуры определяется с точки зрения теста. Тест может запросить фикстуру, только если он находится в области действия, в которой эта фикстура определена. Если фикстура определена внутри класса, её могут запрашивать только тесты внутри этого класса. Но если фикстура определена в глобальной области видимости модуля, её может запросить каждый тест в этом модуле, даже если он определён внутри класса.

Аналогично, автоприменяемая фикстура может повлиять на тест, только если этот тест находится в той же области действия, в которой определена эта автоприменяемая фикстура (см. раздел Автоприменяемые фикстуры выполняются первыми в своей области действия).

Фикстура также может запрашивать любую другую фикстуру, независимо от места её определения, если тест, запрашивающий их, имеет доступ ко всем задействованным фикстурам.

Например, вот файл с тестами, в котором фикстура (outer) запрашивает фикстуру (inner) из области действия, в которой она не была определена:

from __future__ import annotations

import pytest


@pytest.fixture
def order():
    return []


@pytest.fixture
def outer(order, inner):
    order.append("outer")


class TestOne:
    @pytest.fixture
    def inner(self, order):
        order.append("one")

    def test_order(self, order, outer):
        assert order == ["one", "outer"]


class TestTwo:
    @pytest.fixture
    def inner(self, order):
        order.append("two")

    def test_order(self, order, outer):
        assert order == ["two", "outer"]

С точки зрения тестов, все необходимые им фикстуры доступны:

../_images/test_fixtures_request_different_scope.svg

Поэтому при запуске у outer не возникнет проблем с поиском inner, поскольку pytest выполнял поиск с точки зрения тестов.

Примечание

Область действия, в которой определена фикстура, не влияет на порядок её создания: этот порядок определяется логикой, описанной здесь.

conftest.py: совместное использование фикстур в нескольких файлах

Файл conftest.py служит для предоставления фикстур всему каталогу. Фикстуры, определённые в conftest.py, могут использоваться любым тестом в этом пакете без необходимости их импорта (pytest обнаружит их автоматически).

Можно создавать несколько вложенных каталогов/пакетов с тестами, и в каждом каталоге может быть свой файл conftest.py со своими фикстурами, дополняющими фикстуры, предоставляемые файлами conftest.py в родительских каталогах.

Например, для следующей структуры файлов тестов:

tests/
    __init__.py

    conftest.py
        # content of tests/conftest.py
        import pytest

        @pytest.fixture
        def order():
            return []

        @pytest.fixture
        def top(order, innermost):
            order.append("top")

    test_top.py
        # content of tests/test_top.py
        import pytest

        @pytest.fixture
        def innermost(order):
            order.append("innermost top")

        def test_order(order, top):
            assert order == ["innermost top", "top"]

    subpackage/
        __init__.py

        conftest.py
            # content of tests/subpackage/conftest.py
            import pytest

            @pytest.fixture
            def mid(order):
                order.append("mid subpackage")

        test_subpackage.py
            # content of tests/subpackage/test_subpackage.py
            import pytest

            @pytest.fixture
            def innermost(order, mid):
                order.append("innermost subpackage")

            def test_order(order, top):
                assert order == ["mid subpackage", "innermost subpackage", "top"]

Границы областей действия можно представить так:

../_images/fixture_availability.svg

Каталоги становятся своего рода областями действия: фикстуры, определённые в файле conftest.py в конкретном каталоге, становятся доступными во всей этой области.

Тесты могут искать фикстуры вверх по иерархии (выходя за пределы круга), но не могут идти вниз (внутрь круга), продолжая поиск. Поэтому tests/subpackage/test_subpackage.py::test_order сможет найти фикстуру innermost, определённую в tests/subpackage/test_subpackage.py, но фикстура, определённая в tests/test_top.py, будет ему недоступна, поскольку для её поиска пришлось бы перейти на уровень ниже (внутрь круга).

Будет использована первая найденная тестом фикстура, поэтому фикстуры можно переопределять, если нужно изменить или расширить её поведение для определённой области действия.

Файл conftest.py также можно использовать для реализации локальных плагинов для отдельных каталогов.

Фикстуры из сторонних плагинов

Фикстуры не обязательно должны быть определены в этой структуре, чтобы быть доступными тестам. Их также могут предоставлять установленные сторонние плагины — так работает множество плагинов pytest. Если такие плагины установлены, предоставляемые ими фикстуры можно запрашивать из любой части набора тестов.

Поскольку сторонние плагины предоставляют фикстуры извне структуры набора тестов, они не создают область действия, подобную той, которую задают файлы conftest.py и каталоги набора тестов. Поэтому pytest будет искать фикстуры, переходя между областями действия, как описано выше, и обращаться к фикстурам из плагинов только в последнюю очередь.

Например, для следующей структуры файлов:

tests/
    __init__.py

    conftest.py
        # content of tests/conftest.py
        import pytest

        @pytest.fixture
        def order():
            return []

    subpackage/
        __init__.py

        conftest.py
            # content of tests/subpackage/conftest.py
            import pytest

            @pytest.fixture(autouse=True)
            def mid(order, b_fix):
                order.append("mid subpackage")

        test_subpackage.py
            # content of tests/subpackage/test_subpackage.py
            import pytest

            @pytest.fixture
            def inner(order, mid, a_fix):
                order.append("inner subpackage")

            def test_order(order, inner):
                assert order == ["b_fix", "mid subpackage", "a_fix", "inner subpackage"]

Если установлен plugin_a, предоставляющий фикстуру a_fix, и установлен plugin_b, предоставляющий фикстуру b_fix, поиск фикстур тестом будет выглядеть так:

../_images/fixture_availability_plugins.svg

pytest будет искать a_fix и b_fix в плагинах только после того, как сначала выполнит поиск в областях действия внутри tests/.

Примечание

pytest может сообщить, какие фикстуры доступны для заданного теста. Для этого вызовите pytest, указав имя теста (или его область действия), и передайте флаг --fixtures, например pytest --fixtures test_something.py (фикстуры с именами, начинающимися с _, будут показаны только при передаче флага -v).

Порядок создания фикстур

Когда pytest собирается выполнить тест и уже знает, какие фикстуры будут задействованы, ему нужно определить порядок их выполнения. Для этого учитываются три фактора:

  1. область действия
  2. зависимости
  3. автоприменение

Имена фикстур или тестов, место и порядок их определения, а также порядок запроса фикстур не влияют на порядок выполнения — если только это не совпадение. Хотя pytest старается обеспечивать постоянство таких совпадений от запуска к запуску, полагаться на них не следует. Если необходимо управлять порядком, безопаснее всего опираться на эти три фактора и явно задавать зависимости.

Сначала выполняются фикстуры с более широкой областью действия

При запросе фикстур для функции фикстуры с более широкой областью действия (например, session) выполняются раньше фикстур с более узкой областью действия (например, function или class).

Вот пример:

from __future__ import annotations

import pytest


@pytest.fixture(scope="session")
def order():
    return []


@pytest.fixture
def func(order):
    order.append("function")


@pytest.fixture(scope="class")
def cls(order):
    order.append("class")


@pytest.fixture(scope="module")
def mod(order):
    order.append("module")


@pytest.fixture(scope="package")
def pack(order):
    order.append("package")


@pytest.fixture(scope="session")
def sess(order):
    order.append("session")


class TestClass:
    def test_order(self, func, cls, mod, pack, sess, order):
        assert order == ["session", "package", "module", "class", "function"]

Тест пройдёт, потому что фикстуры с более широкой областью действия выполняются первыми.

Порядок будет таким:

../_images/test_fixtures_order_scope.svg

Фикстуры с одинаковой областью действия выполняются с учётом зависимостей

Если фикстура запрашивает другую фикстуру, сначала выполняется запрошенная фикстура. Поэтому, если фикстура a запрашивает фикстуру b, сначала выполнится фикстура b, поскольку a зависит от b и не может работать без неё. Даже если a не нужен результат b, она всё равно может запросить b, если требуется обеспечить её выполнение после b.

Например:

from __future__ import annotations

import pytest


@pytest.fixture
def order():
    return []


@pytest.fixture
def a(order):
    order.append("a")


@pytest.fixture
def b(a, order):
    order.append("b")


@pytest.fixture
def c(b, order):
    order.append("c")


@pytest.fixture
def d(c, b, order):
    order.append("d")


@pytest.fixture
def e(d, b, order):
    order.append("e")


@pytest.fixture
def f(e, order):
    order.append("f")


@pytest.fixture
def g(f, c, order):
    order.append("g")


def test_order(g, order):
    assert order == ["a", "b", "c", "d", "e", "f", "g"]

Если изобразить зависимости, получится примерно такая схема:

../_images/test_fixtures_order_dependencies.svg

Правила, задаваемые каждой фикстурой (то есть то, после каких фикстур она должна выполняться), достаточно полны, чтобы свести схему к следующей линейной последовательности:

../_images/test_fixtures_order_dependencies_flat.svg

Эти запросы должны предоставлять достаточно информации, чтобы pytest мог определить чёткую линейную цепочку зависимостей, а значит, и порядок действий для заданного теста. При наличии неоднозначности, когда порядок действий можно интерпретировать несколькими способами, следует исходить из того, что pytest в любой момент может выбрать любую из таких интерпретаций.

Например, если d не запрашивала бы c, то граф выглядел бы так:

../_images/test_fixtures_order_dependencies_unclear.svg

Поскольку c запрашивала только g, а g также запрашивает f, теперь неясно, должна ли c выполняться до или после f, e или d. Для c установлены только два правила: она должна выполняться после b и до g.

pytest не знает, куда поместить c в этом случае, поэтому следует исходить из того, что она может находиться в любом месте между g и b.

Это не обязательно плохо, но об этом следует помнить. Если порядок выполнения может повлиять на поведение, которое проверяет тест, или иным образом изменить его результат, порядок нужно задать явно так, чтобы pytest мог выстроить его в линейную последовательность.

Автоприменяемые фикстуры выполняются первыми в своей области действия

Считается, что автоприменяемые фикстуры относятся ко всем тестам, которые могут на них ссылаться, поэтому в своей области действия они выполняются раньше других фикстур. Фикстуры, запрашиваемые автоприменяемыми фикстурами, фактически также становятся автоприменяемыми для тех тестов, к которым относится исходная автоприменяемая фикстура.

Поэтому, если фикстура a является автоприменяемой, а фикстура b — нет, но фикстура a запрашивает фикстуру b, то фикстура b фактически тоже станет автоприменяемой, но только для тестов, к которым относится a.

В предыдущем примере граф становился неоднозначным, если d не запрашивала c. Но если бы c была автоприменяемой, то b и a фактически тоже стали бы автоприменяемыми, поскольку от них зависит c. В результате все они переместились бы выше фикстур, не являющихся автоприменяемыми, в пределах этой области действия.

Поэтому, если бы файл с тестами выглядел так:

from __future__ import annotations

import pytest


@pytest.fixture
def order():
    return []


@pytest.fixture
def a(order):
    order.append("a")


@pytest.fixture
def b(a, order):
    order.append("b")


@pytest.fixture(autouse=True)
def c(b, order):
    order.append("c")


@pytest.fixture
def d(b, order):
    order.append("d")


@pytest.fixture
def e(d, order):
    order.append("e")


@pytest.fixture
def f(e, order):
    order.append("f")


@pytest.fixture
def g(f, c, order):
    order.append("g")


def test_order_and_g(g, order):
    assert order == ["a", "b", "c", "d", "e", "f", "g"]

граф выглядел бы так:

../_images/test_fixtures_order_autouse.svg

Теперь c можно разместить в графе выше d, поэтому pytest снова может выстроить граф в линейную последовательность:

../_images/test_fixtures_order_autouse_flat.svg

В этом примере c фактически делает b и a автоприменяемыми фикстурами.

Однако будьте осторожны с автоприменением: автоприменяемая фикстура будет запускаться автоматически для каждого теста, до которого она может дойти, даже если тест её не запрашивает. Например, рассмотрим следующий файл:

from __future__ import annotations

import pytest


@pytest.fixture(scope="class")
def order():
    return []


@pytest.fixture(scope="class", autouse=True)
def c1(order):
    order.append("c1")


@pytest.fixture(scope="class")
def c2(order):
    order.append("c2")


@pytest.fixture(scope="class")
def c3(order, c1):
    order.append("c3")


class TestClassWithC1Request:
    def test_order(self, order, c1, c3):
        assert order == ["c1", "c3"]


class TestClassWithoutC1Request:
    def test_order(self, order, c2):
        assert order == ["c1", "c2"]

Хотя ничто в TestClassWithoutC1Request не запрашивает c1, она всё равно выполняется для содержащихся в нём тестов:

../_images/test_fixtures_order_autouse_multiple_scopes.svg

Однако сам факт того, что одна автоприменяемая фикстура запросила фикстуру, не являющуюся автоприменяемой, не означает, что последняя становится автоприменяемой во всех областях, к которым она может относиться. Фактически она становится автоприменяемой только в тех областях, к которым относится настоящая автоприменяемая фикстура (запросившая фикстуру, не являющуюся автоприменяемой).

Например, рассмотрим следующий файл с тестами:

from __future__ import annotations

import pytest


@pytest.fixture
def order():
    return []


@pytest.fixture
def c1(order):
    order.append("c1")


@pytest.fixture
def c2(order):
    order.append("c2")


class TestClassWithAutouse:
    @pytest.fixture(autouse=True)
    def c3(self, order, c2):
        order.append("c3")

    def test_req(self, order, c1):
        assert order == ["c2", "c3", "c1"]

    def test_no_req(self, order):
        assert order == ["c2", "c3"]


class TestClassWithoutAutouse:
    def test_req(self, order, c1):
        assert order == ["c1"]

    def test_no_req(self, order):
        assert order == []

Его структура будет примерно такой:

../_images/test_fixtures_order_autouse_temp_effects.svg

Для test_req и test_no_req внутри TestClassWithAutouse фикстура c3 фактически делает c2 автоприменяемой фикстурой. Поэтому c2 и c3 выполняются для обоих тестов, несмотря на то, что их не запрашивали; также для test_req c2 и c3 выполняются до c1.

Если бы это сделало c2 действительно автоприменяемой фикстурой, то c2 также выполнялась бы для тестов внутри TestClassWithoutAutouse, поскольку при необходимости они могут обращаться к c2. Но этого не происходит: с точки зрения тестов TestClassWithoutAutouse, c2 не является автоприменяемой фикстурой, поскольку им недоступна c3.

Примечание

pytest может сообщить, в каком порядке будут выполняться фикстуры для заданного теста. Для этого вызовите pytest, указав имя теста (или его область действия), и передайте флаг --setup-plan, например pytest --setup-plan test_something.py (фикстуры с именами, начинающимися с _, будут показаны только при передаче флага -v).

© 2015–2026 Holger Krekel and pytest-dev team
Licensed under the MIT License.
https://docs.pytest.org/en/stable/reference/fixtures.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API