From f2c8a837af2b84d66f225edc399a6a72e160cea7 Mon Sep 17 00:00:00 2001 From: holger krekel Date: Wed, 28 Dec 2011 15:47:19 +0000 Subject: [PATCH] fix issue106: allow parametrize to be applied per-class/per-module --- CHANGELOG | 2 ++ _pytest/mark.py | 19 ------------------- _pytest/python.py | 20 ++++++++++++++++++-- testing/test_mark.py | 14 +++++++------- testing/test_python.py | 17 +++++++++++++++++ 5 files changed, 44 insertions(+), 28 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index bf9b7465d..8089581af 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -7,6 +7,8 @@ Changes between 2.2.1 and 2.2.2.dev produce better output - fix issue102: report more useful errors and hints for when a test directory was renamed and some pyc/__pycache__ remain +- fix issue106: allow parametrize to be applied multiple times + e.g. from module, class and at function level. Changes between 2.2.0 and 2.2.1 ---------------------------------------- diff --git a/_pytest/mark.py b/_pytest/mark.py index 842272e3d..9ab62caf7 100644 --- a/_pytest/mark.py +++ b/_pytest/mark.py @@ -224,22 +224,3 @@ class MarkInfo: for args, kwargs in self._arglist: yield MarkInfo(self.name, args, kwargs) -def pytest_itemcollected(item): - if not isinstance(item, pytest.Function): - return - try: - func = item.obj.__func__ - except AttributeError: - func = getattr(item.obj, 'im_func', item.obj) - pyclasses = (pytest.Class, pytest.Module) - for node in item.listchain(): - if isinstance(node, pyclasses): - marker = getattr(node.obj, 'pytestmark', None) - if marker is not None: - if isinstance(marker, list): - for mark in marker: - mark(func) - else: - marker(func) - node = node.parent - item.keywords.update(py.builtin._getfuncdict(func)) diff --git a/_pytest/python.py b/_pytest/python.py index b1d288345..242d46b21 100644 --- a/_pytest/python.py +++ b/_pytest/python.py @@ -33,7 +33,8 @@ def pytest_generate_tests(metafunc): param = metafunc.function.parametrize except AttributeError: return - metafunc.parametrize(*param.args, **param.kwargs) + for p in param: + metafunc.parametrize(*p.args, **p.kwargs) def pytest_configure(config): config.addinivalue_line("markers", @@ -222,6 +223,7 @@ class PyCollectorMixin(PyobjMixin, pytest.Collector): cls = clscol and clscol.obj or None metafunc = Metafunc(funcobj, config=self.config, cls=cls, module=module) + transfer_markers(metafunc) gentesthook = self.config.hook.pytest_generate_tests extra = [module] if cls is not None: @@ -239,6 +241,20 @@ class PyCollectorMixin(PyobjMixin, pytest.Collector): l.append(function) return l +def transfer_markers(metafunc): + # XXX this should rather be code in the mark plugin or the mark + # plugin should merge with the python plugin. + for holder in (metafunc.cls, metafunc.module): + try: + pytestmark = holder.pytestmark + except AttributeError: + continue + if isinstance(pytestmark, list): + for mark in pytestmark: + mark(metafunc.function) + else: + pytestmark(metafunc.function) + class Module(pytest.File, PyCollectorMixin): def _getobj(self): @@ -559,7 +575,7 @@ class CallSpec2(object): @property def id(self): - return "-".join(filter(None, self._idlist)) + return "-".join(map(str, filter(None, self._idlist))) def setmulti(self, valtype, argnames, valset, id): for arg,val in zip(argnames, valset): diff --git a/testing/test_mark.py b/testing/test_mark.py index 2c8b52db5..e93fe0b7e 100644 --- a/testing/test_mark.py +++ b/testing/test_mark.py @@ -228,26 +228,26 @@ class TestFunctional: keywords = item.keywords marker = keywords['hello'] assert marker.args == ("pos0", "pos1") - assert marker.kwargs == {'x': 3, 'y': 2, 'z': 4} + assert marker.kwargs == {'x': 1, 'y': 2, 'z': 4} # test the new __iter__ interface l = list(marker) assert len(l) == 3 assert l[0].args == ("pos0",) - pytest.xfail(reason="needs reordering of parametrize transfermarks") assert l[1].args == () assert l[2].args == ("pos1", ) - def test_mark_other(self, testdir): - pytest.raises(TypeError, ''' - testdir.getitem(""" + def test_mark_with_wrong_marker(self, testdir): + reprec = testdir.inline_runsource(""" import pytest class pytestmark: pass def test_func(): pass - """) - ''') + """) + l = reprec.getfailedcollections() + assert len(l) == 1 + assert "TypeError" in str(l[0].longrepr) def test_mark_dynamically_in_funcarg(self, testdir): testdir.makeconftest(""" diff --git a/testing/test_python.py b/testing/test_python.py index f2ef56c7f..9db9b446c 100644 --- a/testing/test_python.py +++ b/testing/test_python.py @@ -1048,6 +1048,23 @@ class TestMetafunc: assert metafunc._calls[1].funcargs == dict(x=3, y=4) assert metafunc._calls[1].id == "3-4" + def test_parametrize_multiple_times(self, testdir): + testdir.makepyfile(""" + import pytest + pytestmark = pytest.mark.parametrize("x", [1,2]) + def test_func(x): + assert 0, x + class TestClass: + pytestmark = pytest.mark.parametrize("y", [3,4]) + def test_meth(self, x, y): + assert 0, x + """) + result = testdir.runpytest() + assert result.ret == 1 + result.stdout.fnmatch_lines([ + "*6 fail*", + ]) + class TestMetafuncFunctional: def test_attributes(self, testdir): p = testdir.makepyfile("""