How can I access a pytest fixture by a string?
Asked Answered
N

3

8

pytest fixtures can work on other fixtures by passing them in as argument:

@pytest.fixture(scope='module')
def wrapper_fixture1(fixture1):
    fixture1.do_something()
    return fixture1

Now I have multiple different fixtures fixture1, fixture2 and fixture3 which are different, but have similarities (e.g. a function named do_something()), which I want to apply to each of them.

But instead of defining three new fixtures (like in the example), I would like to define one generic fixture/function that create three fixtures which I can pass to a test. I was thinking about something like this:

def fixture_factory():
    for index in range(3):
        fixture = pytest.get_fixture('fixture%d'%(index+1))
        fixture.do_something()
        pytest.set_fixture('wrapper_fixture%d'%(index+1), fixture, scope='module')

Can this be done in some way? Or do I have to write three wrapper fixtures for each of the original fixtures, repeating the same code over and over?

Nebula answered 10/6, 2014 at 12:9 Comment(0)
L
7

To get a fixture by a string, you can use request.getfuncargvalue() inside a test function or another fixture.

You can try something along those lines:

import pytest

@pytest.fixture
def fixture1():
    return "I'm fixture 1"

@pytest.fixture(scope='module')
def fixture2():
    return "I'm fixture 2"

@pytest.fixture(params=[1, 2])
def all_fixtures(request):
    your_fixture = request.getfuncargvalue("fixture{}".format(request.param))
    # here you can call your_fixture.do_something()
    your_fixture += " with something"
    return your_fixture

def test_all(all_fixtures):
    assert 0, all_fixtures
Lambard answered 21/6, 2014 at 9:22 Comment(2)
Note that request.getfuncargvalue() does indeed work but it is better not to use it if you can. It stops py.test from knowing which tests require which fixtures up front which can have subtle effects and introduce some parameterisation/collection oddities.Aponeurosis
request.getfuncargvalue() is deprecated in favor of request.getfixturevalue(). Though this answer still works. docs.pytest.org/en/latest/…Ornery
A
1

You may also want to look at the pytest_generate_tests() hook (http://pytest.org/latest/plugins.html?highlight=generate_tests#_pytest.hookspec.pytest_generate_tests) for your use case.

Aponeurosis answered 7/7, 2014 at 12:17 Comment(0)
U
1

Up to date answer (Python 3.12, pytest 8.2), based on previous answers.

import pytest

@pytest.fixture
def fixture1():
    yield "I'm fixture 1"

@pytest.fixture(scope='module')
def fixture2():
    yield "I'm fixture 2"


@pytest.fixture(params=["fixture1", "fixture2"])
def fixtures_factory_1(request):
    fixture_value = request.getfixturevalue(request.param)
    yield f"{fixture_value} with something"


@pytest.fixture(
    params=[
        pytest.param("fixture1", id="fix-name-1"),
        pytest.param("fixture2", id="fix-name-2"),
    ]
)
def fixtures_factory_2(request):
    fixture_value = request.getfixturevalue(request.param)
    yield f"{fixture_value} with something"


def test_factory(fixtures_factory_1):
    assert 0, fixtures_factory_1


def test_factory_2(fixtures_factory_2):
    assert 0, fixtures_factory_2

Outcome of test

FAILED test_factory.py::test_factory[fixture1] - AssertionError: I'm fixture 1 with something
FAILED test_factory.py::test_factory[fixture2] - AssertionError: I'm fixture 2 with something
FAILED test_factory.py::test_factory_2[fix-name-1] - AssertionError: I'm fixture 1 with something
FAILED test_factory.py::test_factory_2[fix-name-2] - AssertionError: I'm fixture 2 with something

Using pytest.param allows to have a custom/nice names.

Unsaid answered 15/7 at 15:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.