Is it possible to globally set a default `ids` function for pytest's parametrize?
Question:
pytest.mark.parametrize
accepts an ids
argument which can be a callable, like this:
def test_id_builder(arg):
if isinstance(arg, int):
return str(arg)
... # more logic
@pytest.mark.parametrize('value', [1, 2], ids=test_id_builder)
def test_whatever(value):
assert value > 0
This will generate two test cases, with the ids "1"
and "2"
respectively. The problem is that I have a lot of tests, organized in multiple classes and files. Because of that, I’d like to globally set test_id_builder
as the ids
function for all parametrized tests in my project. Is there a way to do this?
Answers:
There is no way to globally
set ids. but yo can use pytest-generate-tests to generate tests from some other
fixture. that other
fixture could be scoped to session
which overall will mimic the intended behaviour.
You can make your custom parametrize
:
import pytest
def id_builder(arg):
if isinstance(arg, int):
return str(arg) * 2
def custom_parametrize(*args, **kwargs):
kwargs.setdefault('ids', id_builder)
return pytest.mark.parametrize(*args, **kwargs)
@custom_parametrize('value', [1, 2])
def test_whatever(value):
assert value > 0
And to avoid rewriting pytest.mark.parametrize
to custom_parametrize
everywhere use this well-known workaround:
old_parametrize = pytest.mark.parametrize
def custom_parametrize(*args, **kwargs):
kwargs.setdefault('ids', id_builder)
return old_parametrize(*args, **kwargs)
pytest.mark.parametrize = custom_parametrize
Simply implement a custom pytest_make_parametrize_id
hook. In your conftest.py
:
def pytest_make_parametrize_id(config, val, argname):
if isinstance(val, int):
return f'{argname}={val}'
if isinstance(val, str):
return f'text is {val}'
# return None to let pytest handle the formatting
return None
Example tests:
import pytest
@pytest.mark.parametrize('n', range(3))
def test_int(n):
assert True
@pytest.mark.parametrize('s', ('fizz', 'buzz'))
def test_str(s):
assert True
@pytest.mark.parametrize('c', (tuple(), list(), set()))
def test_unhandled(c):
assert True
Check the test parametrizing:
$ pytest -q --collect-only
test_spam.py::test_int[n=0]
test_spam.py::test_int[n=1]
test_spam.py::test_int[n=2]
test_spam.py::test_str[text is fizz]
test_spam.py::test_str[text is buzz]
test_spam.py::test_unhandled[c0]
test_spam.py::test_unhandled[c1]
test_spam.py::test_unhandled[c2]
no tests ran in 0.06 seconds
pytest.mark.parametrize
accepts an ids
argument which can be a callable, like this:
def test_id_builder(arg):
if isinstance(arg, int):
return str(arg)
... # more logic
@pytest.mark.parametrize('value', [1, 2], ids=test_id_builder)
def test_whatever(value):
assert value > 0
This will generate two test cases, with the ids "1"
and "2"
respectively. The problem is that I have a lot of tests, organized in multiple classes and files. Because of that, I’d like to globally set test_id_builder
as the ids
function for all parametrized tests in my project. Is there a way to do this?
There is no way to globally
set ids. but yo can use pytest-generate-tests to generate tests from some other
fixture. that other
fixture could be scoped to session
which overall will mimic the intended behaviour.
You can make your custom parametrize
:
import pytest
def id_builder(arg):
if isinstance(arg, int):
return str(arg) * 2
def custom_parametrize(*args, **kwargs):
kwargs.setdefault('ids', id_builder)
return pytest.mark.parametrize(*args, **kwargs)
@custom_parametrize('value', [1, 2])
def test_whatever(value):
assert value > 0
And to avoid rewriting pytest.mark.parametrize
to custom_parametrize
everywhere use this well-known workaround:
old_parametrize = pytest.mark.parametrize
def custom_parametrize(*args, **kwargs):
kwargs.setdefault('ids', id_builder)
return old_parametrize(*args, **kwargs)
pytest.mark.parametrize = custom_parametrize
Simply implement a custom pytest_make_parametrize_id
hook. In your conftest.py
:
def pytest_make_parametrize_id(config, val, argname):
if isinstance(val, int):
return f'{argname}={val}'
if isinstance(val, str):
return f'text is {val}'
# return None to let pytest handle the formatting
return None
Example tests:
import pytest
@pytest.mark.parametrize('n', range(3))
def test_int(n):
assert True
@pytest.mark.parametrize('s', ('fizz', 'buzz'))
def test_str(s):
assert True
@pytest.mark.parametrize('c', (tuple(), list(), set()))
def test_unhandled(c):
assert True
Check the test parametrizing:
$ pytest -q --collect-only
test_spam.py::test_int[n=0]
test_spam.py::test_int[n=1]
test_spam.py::test_int[n=2]
test_spam.py::test_str[text is fizz]
test_spam.py::test_str[text is buzz]
test_spam.py::test_unhandled[c0]
test_spam.py::test_unhandled[c1]
test_spam.py::test_unhandled[c2]
no tests ran in 0.06 seconds