pythonpytest

aggregate or split pytest fixtures


Two related pytest questions:

Split fixture into individual values

I have a fixture which is an iterable and which is tested in a certain test. This works. However, I also have a test which wants to test the individual values - I don't know how to do this.

For example, how can I write the student fixture such that it returns each of the elements of klass?

@pytest.fixture
def klass() -> list[str]:
    return ['alice', 'bob', 'claire']

@pytest.fixture
def student(klass) -> str:               # HERE IS THE QUESTION
    #code that returns each student in the klass individually


def test_klass(klass):  # ran once
    assert len(klass) > 0

def test_student(student):  # ran 3 times
    assert isinstance(student, str)

Combine individual fixture values into single fixture

The inverse problem occurs if I want to specify the students and combine them into a klass (the same test functions apply as above):

@pytest.fixture(params = ['alice', 'bob', 'claire'])
def student(request) -> str:
    return request.param

@pytest.fixture
def klass(student) -> list[str]:          # HERE IS THE QUESTION
    #code that returns the list with all values for student

Solution

  • Testing individual values of an iterable in pytest is done by parametrizing the test.

    An easy way to adapt your test is to use a function that returns the parameters, which is used both in the fixture, and by pytest.mark.parametrize (or by the parametrized fixture, whichever you prefer):

    import pytest
    
    def students() -> list[str]:
        return ['alice', 'bob', 'claire']
    
    @pytest.fixture
    def klass() -> list[str]:
        return students()
    
    @pytest.fixture(params=students())
    def student(request) -> str:
        return request.param
    
    def test_klass(klass):  # runs once
        assert len(klass) > 0
    
    @pytest.mark.parametrize('s', students())
    def test_student(s):  # runs 3 times
        assert isinstance(s, str)
    
    def test_student2(student):  # runs 3 times
        assert isinstance(student, str)
    

    Note that you cannot use a fixture in parametrize (or in params in the parametrized fixture) out of the box, so the iterable has to be defined in a function. As long as the function doesn't do extensive calculations, which would be run twice, this should be fine (otherwise the iterable may be cached).