How to concatenate several parametrized fixtures into a new fixture in py.test? How to concatenate several parametrized fixtures into a new fixture in py.test? python python

How to concatenate several parametrized fixtures into a new fixture in py.test?


There is now a solution available in pytest-cases, named fixture_union. Here is how you create the fixture union that you requested in your example:

from pytest_cases import fixture_union, pytest_fixture_plus@pytest_fixture_plus(params=[1, 2, 3])def lower(request):    return "i" * request.param@pytest_fixture_plus(params=[1, 2])def upper(request):    return "I" * request.paramfixture_union('all', ['lower', 'upper'])def test_all(all):    print(all)

It works as expected:

<...>::test_all[lower-1] <...>::test_all[lower-2] <...>::test_all[lower-3] <...>::test_all[upper-1] <...>::test_all[upper-2] 

Note that I used pytest_fixture_plus in the above example because if you use pytest.fixture you will have to handle yourself the cases where a fixture is not actually used. This is done as follows, for example for the upper fixture:

import pytestfrom pytest_cases import NOT_USED@pytest.fixture(params=[1, 2])def upper(request):    # this fixture does not use pytest_fixture_plus     # so we have to explicitly discard the 'NOT_USED' cases    if request.param is not NOT_USED:        return "I" * request.param

See documentation for details. (I'm the author by the way ;) )


I had the exact same question (and received a similar, but distinct answer). The best solution I was able to come up with was to reconsider how I parametrize my tests. Instead of having multiple fixtures with compatible outputs, I used the fixtures as regular functions, and just parametrized your meta-fixture to accept the function name and arguments:

import pytestdef lower(n):    return 'i' * ndef upper(n):    return 'I' * n@pytest.fixture(params=[    (lower, 1),    (lower, 2),    (upper, 1),    (upper, 2),    (upper, 3),])def all(request):    func, *n = request.param    return func(*n)def test_all(all):    ...

In your particular case, unpacking n into a list and passing it with * is slightly overkill, but it provides generality. My case has fixtures that all accept different parameter lists.

Until pytest allows us to properly chain fixtures, this is the only way I have come up with to run 5 tests instead of 12 in your situation. You can make the list shorter with something like

@pytest.fixture(params=[    *[(lower, i) for i in range(1, 3)],    *[(upper, i) for i in range(1, 4)],])

There is an actual advantage of doing it this way. You can pick and chose which tests you want to do special things to, like XFAIL, without affecting a whole swath of other tests if you have additional dependencies in your pipeline.


It is not beautiful, but may be today you know the better way.

Request object inside 'all' fixture know only about own params: 'lower', 'upper'. One way using fixtures from a fixture function.

import pytest@pytest.fixture(params=[1, 2, 3])def lower(request):    return "i" * request.param@pytest.fixture(params=[1, 2])def upper(request):    return "I" * request.param@pytest.fixture(params=['lower', 'upper'])def all(request, lower, upper):    if request.param == 'lower':        return lower    else:        return upperdef test_all(all):    assert 0, all