3
votes

I'd like to get some help on how to run pytests with multiple layers of parameterized pytest fixtures.

I have a global request-based fixture for selecting a base implementation of the system I'd like to test:

@fixture()
def a_provider():
    yield 'a'


@fixture()
def b_provider():
    yield 'b'


@fixture(params=["a_provider", "b_provider"])
def letter_provider(request):
    yield request.getfixturevalue(request.param)

I'd like to now run some additionally-parametrized tests against different test cases on the backend.


@fixture()
def one(letter_provider):
    yield letter_provider * 1


@fixture()
def two(letter_provider):
    yield letter_provider * 2


@fixture(params=["one", "two"])
def letter_cases(request):
    yield request.getfixturevalue(request.param)

Using these two layers of fixtures, I'd like to be able to run both tests parameterised by the base-implementation.

def test_foo(letter_provider):
    """
    Should run once for "a" and once for "b"
    """
    assert letter_provider in {"a", "b"}

And tests that use all the additional cases

def test_bar(letter_cases):
    """
    test should be executed 4 times for : a, aa, b, bb
    """
    assert letter_cases in {
        "a", "aa",
        "b", "bb",
    }

At the moment, my test setup fails with:

test setup failed
'letter_provider'

The above exception was the direct cause of the following exception:
letter_provider

During handling of the above exception, another exception occurred:
The requested fixture has no parameter defined for test:
    tests/test_example.py::test_bar[one]

Requested fixture 'letter_provider' defined in:
tests/test_example.py:15

How could I set up my test suite to run these levels of parameterization?

1
I can't test that right now, but IIRC this can be fixed by providing the dependency fixtures in the fixture signature, e.g. def letter_cases(request, one, two): .... - hoefling
@hoefling this works. If you'd like to post an answer, I'll happily accept it. - Xandros

1 Answers

3
votes

Ensure the fixture cache is filled with one and two fixture values when calling getfixturevalue() by adding both fixtures as letter_cases dependencies:

@fixture(params=["one", "two"])
def letter_cases(request, one, two):
    yield request.getfixturevalue(request.param)