1

I want to be able to parametrize a fixture list and list within a list for a unit-test like in the sample code below, the problem is that the fixtures are not recognized in the "pytest.mark.parametrize" annotation. What should I do?

@pytest.fixture(scope="session")
def simple_list() -> list[int]:
    return [1, 2, 3]


@pytest.fixture(scope="session")
def list_within_a_list() -> list[list[int]]:
    return [[1, 2, 3], [3, 4, 5], [5, 6, 7]]


@pytest.mark.parametrize(
    "simple_list_element, list_within_a_list_first_element, list_within_a_list_second_element, list_within_a_list_third_element",
    itertools.product(
        simple_list,
        *list_within_a_list,
    ),
)
def test_check_settings(
    simple_list_element,
    list_within_a_list_first_element,
    list_within_a_list_second_element,
    list_within_a_list_third_element,
):
    ...
  • 3
    does this help? https://stackoverflow.com/a/64348247/202168 – Anentropic Jul 27 '22 at 11:23
  • @Anentropic I also want to be able to reuse these fixtures for a different test as well, how will that work, without having redundancies? – Jordan Jordanovski Jul 27 '22 at 11:55
  • 1
    the answer I linked is showing how to use fixtures as params in a parametrized test, the fixtures themselves remain normal fixtures – Anentropic Jul 27 '22 at 11:56
  • Oh right, but any idea how can I achieve the cross product that I want from the two lists? As you can see there is a simple one dimensional list (simple_list) and another two dimensional list (list_within_a_list) from which I expect to get the four parameters. – Jordan Jordanovski Jul 27 '22 at 12:08

1 Answers1

1

I think this should do what you want:

@pytest.fixture(scope="session")
def simple_list() -> list[int]:
    return [1, 2, 3]


@pytest.fixture(scope="session")
def list_within_a_list() -> list[list[int]]:
    return [[1, 2, 3], [3, 4, 5], [5, 6, 7]]


@pytest.fixture(scope="session")
def list_within_a_list_first(list_within_a_list) -> list[int]:
    return list_within_a_list[0]


@pytest.fixture(scope="session")
def list_within_a_list_second(list_within_a_list) -> list[int]:
    return list_within_a_list[1]


@pytest.fixture(scope="session")
def list_within_a_list_third(list_within_a_list) -> list[int]:
    return list_within_a_list[2]


@pytest.mark.parametrize("simple_list_fixture", ["simple_list"])
@pytest.mark.parametrize("list_within_a_list_first_fixture", ["list_within_a_list_first"])
@pytest.mark.parametrize("list_within_a_list_second_fixture", ["list_within_a_list_second"])
@pytest.mark.parametrize("list_within_a_list_third_fixture", ["list_within_a_list_third"])
def test_check_settings(
    simple_list_fixture,
    list_within_a_list_first_fixture,
    list_within_a_list_second_fixture,
    list_within_a_list_third_fixture,
):
    simple_list_element = request.getfixturevalue(simple_list_fixture)
    list_within_a_list_first_element = request.getfixturevalue(list_within_a_list_first_fixture)
    list_within_a_list_second_element = request.getfixturevalue(list_within_a_list_second_fixture)
    list_within_a_list_third_element = request.getfixturevalue(list_within_a_list_third_fixture)
    ...

Applying the parametrize decorator multiple times gives you the product of the params.

If list_within_a_list returns a variable number of elements or lots of elements and you can't manually derive the per-element fixtures from it then I think you'd need to look into using subtests, via pytest-subtests lib.

In that case I think you could do this:

@pytest.fixture(scope="session")
def simple_list() -> list[int]:
    return [1, 2, 3]


@pytest.fixture(scope="session")
def list_within_a_list() -> list[list[int]]:
    return [[1, 2, 3], [3, 4, 5], [5, 6, 7]]


@pytest.mark.parametrize(
    "simple_list_fixture, list_within_a_list_fixture",
    ["simple_list", "list_within_a_list"]
)
def test_check_settings(
    subtests,
    simple_list_fixture,
    list_within_a_list_fixture,
):
    simple_list_element = request.getfixturevalue(simple_list_fixture)
    list_within_a_list_elements = request.getfixturevalue(list_within_a_list_fixture)
    values_gen = itertools.product(simple_list_element, *list_within_a_list_elements)
    for i, values in enumerate(values_gen):
        with subtests.test(msg=f"case {i}", i=i, values=values):
            simple_el, *list_els = values
            ...
Anentropic
  • 32,188
  • 12
  • 99
  • 147