Merge pull request #1535 from palaviv/parametrize-test-ids-hook

introduce pytest_make_parametrize_id hook
This commit is contained in:
Ronny Pfannschmidt 2016-04-27 16:16:16 +02:00
commit 6cc56b4a1b
5 changed files with 41 additions and 7 deletions

View File

@ -27,7 +27,8 @@
whether to filter the traceback based on the ``ExceptionInfo`` object passed whether to filter the traceback based on the ``ExceptionInfo`` object passed
to it. to it.
* * New ``pytest_make_parametrize_id`` hook.
Thanks `@palaviv`_ for the PR.
**Changes** **Changes**

View File

@ -156,6 +156,12 @@ def pytest_pyfunc_call(pyfuncitem):
def pytest_generate_tests(metafunc): def pytest_generate_tests(metafunc):
""" generate (multiple) parametrized calls to a test function.""" """ generate (multiple) parametrized calls to a test function."""
@hookspec(firstresult=True)
def pytest_make_parametrize_id(config, val):
"""Return a user-friendly string representation of the given ``val`` that will be used
by @pytest.mark.parametrize calls. Return None if the hook doesn't know about ``val``.
"""
# ------------------------------------------------------------------------- # -------------------------------------------------------------------------
# generic runtest related hooks # generic runtest related hooks
# ------------------------------------------------------------------------- # -------------------------------------------------------------------------

View File

@ -342,6 +342,9 @@ def pytest_pycollect_makeitem(collector, name, obj):
res = list(collector._genfunctions(name, obj)) res = list(collector._genfunctions(name, obj))
outcome.force_result(res) outcome.force_result(res)
def pytest_make_parametrize_id(config, val):
return None
def is_generator(func): def is_generator(func):
try: try:
return _pytest._code.getrawcode(func).co_flags & 32 # generator function return _pytest._code.getrawcode(func).co_flags & 32 # generator function
@ -1038,7 +1041,7 @@ class Metafunc(FuncargnamesCompatAttr):
if ids and len(ids) != len(argvalues): if ids and len(ids) != len(argvalues):
raise ValueError('%d tests specified with %d ids' %( raise ValueError('%d tests specified with %d ids' %(
len(argvalues), len(ids))) len(argvalues), len(ids)))
ids = idmaker(argnames, argvalues, idfn, ids) ids = idmaker(argnames, argvalues, idfn, ids, self.config)
newcalls = [] newcalls = []
for callspec in self._calls or [CallSpec2(self)]: for callspec in self._calls or [CallSpec2(self)]:
for param_index, valset in enumerate(argvalues): for param_index, valset in enumerate(argvalues):
@ -1138,7 +1141,7 @@ else:
return val.encode('unicode-escape') return val.encode('unicode-escape')
def _idval(val, argname, idx, idfn): def _idval(val, argname, idx, idfn, config=None):
if idfn: if idfn:
try: try:
s = idfn(val) s = idfn(val)
@ -1147,6 +1150,11 @@ def _idval(val, argname, idx, idfn):
except Exception: except Exception:
pass pass
if config:
hook_id = config.hook.pytest_make_parametrize_id(config=config, val=val)
if hook_id:
return hook_id
if isinstance(val, (bytes, str)) or (_PY2 and isinstance(val, unicode)): if isinstance(val, (bytes, str)) or (_PY2 and isinstance(val, unicode)):
return _escape_strings(val) return _escape_strings(val)
elif isinstance(val, (float, int, bool, NoneType)): elif isinstance(val, (float, int, bool, NoneType)):
@ -1159,16 +1167,16 @@ def _idval(val, argname, idx, idfn):
return val.__name__ return val.__name__
return str(argname)+str(idx) return str(argname)+str(idx)
def _idvalset(idx, valset, argnames, idfn, ids): def _idvalset(idx, valset, argnames, idfn, ids, config=None):
if ids is None or ids[idx] is None: if ids is None or ids[idx] is None:
this_id = [_idval(val, argname, idx, idfn) this_id = [_idval(val, argname, idx, idfn, config)
for val, argname in zip(valset, argnames)] for val, argname in zip(valset, argnames)]
return "-".join(this_id) return "-".join(this_id)
else: else:
return _escape_strings(ids[idx]) return _escape_strings(ids[idx])
def idmaker(argnames, argvalues, idfn=None, ids=None): def idmaker(argnames, argvalues, idfn=None, ids=None, config=None):
ids = [_idvalset(valindex, valset, argnames, idfn, ids) ids = [_idvalset(valindex, valset, argnames, idfn, ids, config)
for valindex, valset in enumerate(argvalues)] for valindex, valset in enumerate(argvalues)]
if len(set(ids)) != len(ids): if len(set(ids)) != len(ids):
# The ids are not unique # The ids are not unique

View File

@ -470,6 +470,7 @@ you can use the following hook:
.. autofunction:: pytest_pycollect_makeitem .. autofunction:: pytest_pycollect_makeitem
.. autofunction:: pytest_generate_tests .. autofunction:: pytest_generate_tests
.. autofunction:: pytest_make_parametrize_id
After collection is complete, you can modify the order of After collection is complete, you can modify the order of
items, delete or otherwise amend the test items: items, delete or otherwise amend the test items:

View File

@ -1156,3 +1156,21 @@ class TestMarkersWithParametrization:
""") """)
reprec = testdir.inline_run() reprec = testdir.inline_run()
reprec.assertoutcome(passed=2) reprec.assertoutcome(passed=2)
def test_pytest_make_parametrize_id(self, testdir):
testdir.makeconftest("""
def pytest_make_parametrize_id(config, val):
return str(val * 2)
""")
testdir.makepyfile("""
import pytest
@pytest.mark.parametrize("x", range(2))
def test_func(x):
pass
""")
result = testdir.runpytest("-v")
result.stdout.fnmatch_lines([
"*test_func*0*PASS*",
"*test_func*2*PASS*",
])