pythonpytestpytest-parametrization

Is there a callable for generating ids with `pytest.fixture(param=...)` the same way it would be generated with `pytest.mark.parametrize()`?


I'm using parametrized fixtures but I don't find the way ids are generated practical.

I'd like to fall back on the way it's generated when using pytest.mark.parametrize.

I've seen that it's possible to provide a callable as the ids keyword argument in pytest.fixture (and it works), but I was wondering if there was already an implemented callable that could serve this specific purpose. Is there some internal I could replace get_id with?

I include a MRE for illustrating my issue below.


MRE:

import pytest

def add3(a, b, c):
    return a + b + c

@pytest.mark.parametrize("a,b,c", [
    (1, 2, 3),
    (4, 5, 6),
])
def test_add_with_parametrize(a, b, c):
    assert a + b + c == add3(a, b, c)


@pytest.fixture(params=[(1, 2, 3), (4, 5, 6)])
def parametrized_fixture(request):
    return request.param

def test_add_with_parametrized_fixture(parametrized_fixture):
    a, b, c = parametrized_fixture
    assert a + b + c == add3(a, b, c)


def get_id(val):
    return f"{val!r}"

@pytest.fixture(params=[(1, 2, 3), (4, 5, 6)], ids=get_id)
def parametrized_fixture_bis(request):
    return request.param

def test_add_with_parametrized_fixture_bis(parametrized_fixture_bis):
    a, b, c = parametrized_fixture_bis
    assert a + b + c == add3(a, b, c)
pytest -v
============================= test session starts =============================
platform linux -- Python 3.11.11, pytest-8.3.5, pluggy-1.5.0 -- /home/vmonteco/.pyenv/versions/3.11.11/envs/3.11_pytest/bin/python
cachedir: .pytest_cache
rootdir: /home/vmonteco/code/MREs/MRE_pytest_ids
collected 6 items                                                             

test_ids.py::test_add_with_parametrize[1-2-3] PASSED                    [ 16%]
test_ids.py::test_add_with_parametrize[4-5-6] PASSED                    [ 33%]
test_ids.py::test_add_with_parametrized_fixture[parametrized_fixture0] PASSED [ 50%]
test_ids.py::test_add_with_parametrized_fixture[parametrized_fixture1] PASSED [ 66%]
test_ids.py::test_add_with_parametrized_fixture_bis[(1, 2, 3)] PASSED   [ 83%]
test_ids.py::test_add_with_parametrized_fixture_bis[(4, 5, 6)] PASSED   [100%]

============================== 6 passed in 0.01s ==============================

Solution

  • The change you observe in test ids generation isn't due to the use of parametrized fixtures, but to the way pytest generates these ids depending on the parameters types:

    Numbers, strings, booleans and None will have their usual string representation used in the test ID. For other objects, pytest will make a string based on the argument name:

    In your MRE, while you were passing parameters as integers in your first test, you're now passing them as a tuple in your second one, so the id is now based on the argument name as stated above.

    def test_add_with_parametrize(a: int, b: int, c: int): ...
    def test_add_with_parametrized_fixture(parametrized_fixture: tuple): ...
    

    For types that aren't numbers, strings, booleans or None, you can either provide a callable to generate these ids as you mentioned in your post or you can override the ids generation behavior with the pytest_make_parametrize_id hook in conftest.py

    def pytest_make_parametrize_id(config, val, argname):
        return f"{val!r}"
    

    test_ids.py:

    import pytest
    
    
    def add3(a, b, c):
        return a + b + c
    
    
    @pytest.mark.parametrize("a,b,c", [
        (1, 2, 3),
        (4, 5, 6),
    ])
    def test_add_with_parametrize(a, b, c):
        assert a + b + c == add3(a, b, c)
    
    
    @pytest.fixture(params=[(1, 2, 3), (4, 5, 6)])
    def parametrized_fixture(request):
        return request.param
    
    
    def test_add_with_parametrized_fixture(parametrized_fixture):
        a, b, c = parametrized_fixture
        assert a + b + c == add3(a, b, c)
    
    
    @pytest.fixture(params=[(1, 2, 3), (4, 5, 6)])
    def parametrized_fixture_bis(request):
        return request.param
    
    
    def test_add_with_parametrized_fixture_bis(parametrized_fixture_bis):
        a, b, c = parametrized_fixture_bis
        assert a + b + c == add3(a, b, c)
    

    Output

    test_ids.py::test_add_with_parametrize[1-2-3] PASSED               [ 16%]
    test_ids.py::test_add_with_parametrize[4-5-6] PASSED               [ 33%]
    test_ids.py::test_add_with_parametrized_fixture[(1, 2, 3)] PASSED  [ 50%]
    test_ids.py::test_add_with_parametrized_fixture[(4, 5, 6)] PASSED  [ 66%]
    test_ids.py::test_add_with_parametrized_fixture_bis[(1, 2, 3)] PASSED [ 83%]
    test_ids.py::test_add_with_parametrized_fixture_bis[(4, 5, 6)] PASSED [100%]