Search code examples
unit-testingpytestfixtures

Parametrizing tests depending of also parametrized values in pytest


I have a test where I have a setup method, that should receive a dataset, and a test function, that should run for each data in dataset

Basically I would need something like:

datasetA = [data1_a, data2_a, data3_a]
datasetB = [data1_b, data2_b, data3_b]

@pytest.fixture(autouse=True, scope="module", params=[datasetA, datasetB])
def setup(dataset):
    #do setup
    yield
    #finalize

#dataset should be the same instantiated for the setup
@pytest.mark.parametrize('data', [data for data in dataset]) 
def test_data(data):
    #do test

It should run like:

  • setup(datasetA)
  • test(data1_a)
  • test(data2_a)
  • test(data3_a)
  • setup(datasetB)
  • test(data1_b)
  • test(data2_b)
  • test(data3_b)

However it does not seem to be possible to parametrize over a variable obtained by a fixture, as I wanted to in the example.

I could have my function use a fixture and iterate inside the test method:

def test_data(dataset):
    for data in dataset:
        #do test

But then I would have one large test instead of a separate test for each case, which I would not like to have.

Is there any way of accomplishing this?

Thanks!


Solution

  • Answer #1: If strictly following you test design, then it should look like this:

    import pytest
    
    datasetA = [10, 20, 30]
    datasetB = [100, 200, 300]
    
    @pytest.fixture
    def dataset(request):
        #do setup
        items = request.param
        yield items
        #finalize
    
    @pytest.fixture
    def item(request, dataset):
        index = request.param
        yield dataset[index]
    
    #dataset should be the same instantiated for the setup
    @pytest.mark.parametrize('dataset', [datasetA, datasetB], indirect=True)
    @pytest.mark.parametrize('item', [0, 1, 2], indirect=True)
    def test_data(dataset, item):
        print(item)
        #do test
    

    Note the indirect parametrization for both item & dataset. The parameter values will be passed to the same-named fixture as request.param. In this case, we use the index in the assumption that the datasets are of the same length of 3 items.

    Here is how it executes:

    $ pytest -s -v -ra test_me.py 
    test_me.py::test_data[0-dataset0] 10
    PASSED
    test_me.py::test_data[0-dataset1] 100
    PASSED
    test_me.py::test_data[1-dataset0] 20
    PASSED
    test_me.py::test_data[1-dataset1] 200
    PASSED
    test_me.py::test_data[2-dataset0] 30
    PASSED
    test_me.py::test_data[2-dataset1] 300
    PASSED