pythonpytestparametrized-testingpytest-fixtures

How to combine fixture with parametrize


I have comfortably been using @pytest.mark.parametrize() in many tests now. I have not, however, succeeded in combining it with @pytest.fixture() and I cannot find a question answering this issue.

This example applies @pytest.fixture() succesfully (copied from another question that I cannot find anymore):

import pytest
def foo(data, key):
    return data[key]

@pytest.fixture()
def dict_used_by_many_tests():
    return {"name": "Dionysia", "age": 28, "location": "Athens"}

def test_foo_one(dict_used_by_many_tests):
    actual = foo(dict_used_by_many_tests, "name")
    expected = "Dionysia"
    assert actual == expected

Now, in practice I want to use @pytest.mark.parametrize().

@pytest.fixture()
def dict_used_by_many_tests():
    return {"name": "Dionysia", "age": 28, "location": "Athens"}

@pytest.mark.parametrize(
    "dict_used_by_many_tests",
    [
        (dict_used_by_many_tests),
    ],
)
def test_foo_one(dict_used_by_many_tests):
    actual = foo(dict_used_by_many_tests, "name")
    expected = "Dionysia"
    assert actual == expected

This results in the error: TypeError: 'function' object is not subscriptable.

I tried calling dict_used_by_many_tests() to work with its return value instead of the function object. This resulted in a Fixtures are not meant to be called directly error, however.


Solution

  • One method is request.getfixturevalue:

    import pytest
    
    
    @pytest.fixture()
    def dict_used_by_many_tests():
        return {
            "name": "Dionysia",
            "age": 28,
            "location": "Athens",
        }
    
    
    @pytest.fixture()
    def dict_used_by_some_tests():
        return {
            "name": "Medusa, maybe?",
            "age": 2 << 32,
            "location": "Underworld?",
        }
    
    
    @pytest.mark.parametrize(
        "fixture_name",
        [
            "dict_used_by_many_tests",
            "dict_used_by_some_tests",
        ],
    )
    def test_foo_one(request, fixture_name):
        d = request.getfixturevalue(fixture_name)
        # ...
    

    Another is indirect parametrization:

    import pytest
    
    
    @pytest.fixture()
    def character_dict(request):
        if request.param == 1:
            return {"name": "Dionysia", "age": 28, "location": "Athens"}
        elif request.param == 2:
            return {"name": "Medusa", "age": 2 << 32, "location": "Underworld"}
        raise NotImplementedError(f"No such dict: {request.param!r}")
    
    
    @pytest.mark.parametrize(
        "character_dict",
        [1, 2],
        indirect=True,
    )
    def test_foo_one(character_dict):
        assert isinstance(character_dict, dict)