Ссылка на светильники

См.также

О светильниках

Встроенные светильники

Fixtures определяются с помощью декоратора @pytest.fixture. Pytest имеет несколько полезных встроенных приспособлений:

capfd

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

capfdbinary

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

caplog

Контролируйте ведение журнала и записи журнала доступа.

capsys

Захват, как текст, вывод на sys.stdout и sys.stderr.

capsysbinary

Захватите в виде байтов вывод на sys.stdout и sys.stderr.

cache

Храните и извлекайте значения во время выполнения pytest.

doctest_namespace

Предоставьте дикту, инжектированную в пространство имен docstests.

monkeypatch

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

pytestconfig

Доступ к значениям конфигурации, pluginmanager и крючкам плагинов.

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.

Наличие приспособлений

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

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

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

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

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 искал с точки зрения тестов.

Примечание

Область видимости, в которой определено приспособление, не влияет на порядок, в котором оно будет инстанцировано: порядок определяется логикой, описанной here.

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, будет для него недоступно, потому что ему придется спуститься на уровень ниже (шагнуть внутрь круга), чтобы найти его.

Первое приспособление, которое находит тест, и будет использоваться, поэтому fixtures can be overridden, если вам нужно изменить или расширить то, что делает одно приспособление для определенного диапазона.

Вы также можете использовать файл conftest.py для реализации local per-directory plugins.

Приспособления из плагинов сторонних производителей

Для того чтобы быть доступными для тестов, приспособления не обязательно должны быть определены в этой структуре. Они также могут быть предоставлены установленными сторонними плагинами, и именно так работают многие плагины 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 хочет выполнить тест, после того, как он узнает, какие фикстуры будут выполнены, ему нужно определить порядок их выполнения. Для этого он учитывает 3 фактора:

  1. сфера применения

  2. зависимости

  3. autouse

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

Более высокоскопированные приспособления выполняются первыми

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

Вот пример:

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.

Например:

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(a, 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 мог линеаризовать/»сгладить» этот порядок.

Фиксаторы Autouse выполняются первыми в пределах своей области действия

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

Так, если приспособление a является autouse, а приспособление b - нет, но приспособление a запрашивает приспособление b, то приспособление b фактически будет также приспособлением autouse, но только для тестов, к которым применяется a.

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

Итак, если тестовый файл выглядел следующим образом:

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 эффективными автодоводчиками.

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

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

Но только потому, что одно автоумное приспособление запросило неавтоумное приспособление, это не означает, что неавтоумное приспособление становится автоумным приспособлением для всех контекстов, к которым оно может применяться. Оно становится автоприспособлением только для тех контекстов, к которым может быть применено настоящее автоприспособление (то, которое запросило неавторское приспособление).

Например, посмотрите на этот тестовый файл:

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 приспособлением autouse, вот почему c2 и c3 выполняются для обоих тестов, несмотря на отсутствие запроса, и почему c2 и c3 выполняются перед c1 для test_req.

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

Вернуться на верх