From 2fc7aede0b507b93d02fb1222fd5b2e3f82e3167 Mon Sep 17 00:00:00 2001 From: elizabeth Date: Sun, 2 Aug 2015 16:40:40 +0300 Subject: [PATCH] Request #714: Apply indirect=True on particular argnames --- _pytest/python.py | 22 ++++++++++++++++----- testing/python/metafunc.py | 40 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 5 deletions(-) diff --git a/_pytest/python.py b/_pytest/python.py index 4623d3d04..41c5e5cf2 100644 --- a/_pytest/python.py +++ b/_pytest/python.py @@ -761,11 +761,12 @@ class CallSpec2(object): def id(self): return "-".join(map(str, filter(None, self._idlist))) - def setmulti(self, valtype, argnames, valset, id, keywords, scopenum, + def setmulti(self, valtypes, argnames, valset, id, keywords, scopenum, param_index): for arg,val in zip(argnames, valset): self._checkargnotcontained(arg) - getattr(self, valtype)[arg] = val + valtype_for_arg = valtypes[arg] + getattr(self, valtype_for_arg)[arg] = val self.indices[arg] = param_index self._arg2scopenum[arg] = scopenum if val is _notexists: @@ -844,7 +845,9 @@ class Metafunc(FuncargnamesCompatAttr): where each tuple-element specifies a value for its respective argname. - :arg indirect: if True each argvalue corresponding to an argname will + :arg indirect: The list of argnames or boolean. A list of arguments' + names (subset of argnames). If True the list contains all names from + the argnames. Each argvalue corresponding to an argname in this list will be passed as request.param to its respective argname fixture function so that it can perform more expensive setups during the setup phase of a test rather than at collection time. @@ -889,13 +892,22 @@ class Metafunc(FuncargnamesCompatAttr): if scope is None: scope = "function" scopenum = scopes.index(scope) + valtypes = {arg: "funcargs" for arg in argnames} if not indirect: #XXX should we also check for the opposite case? for arg in argnames: if arg not in self.fixturenames: raise ValueError("%r uses no fixture %r" %( self.function, arg)) - valtype = indirect and "params" or "funcargs" + else: + if not isinstance(indirect, (tuple, list)): + valtypes = {arg: "params" for arg in argnames} + else: + for arg in indirect: + if arg not in argnames: + raise ValueError("indirect: fixture %r doesn't exist" %( + arg)) + valtypes[arg] = "params" idfn = None if callable(ids): idfn = ids @@ -910,7 +922,7 @@ class Metafunc(FuncargnamesCompatAttr): for param_index, valset in enumerate(argvalues): assert len(valset) == len(argnames) newcallspec = callspec.copy(self) - newcallspec.setmulti(valtype, argnames, valset, ids[param_index], + newcallspec.setmulti(valtypes, argnames, valset, ids[param_index], newkeywords.get(param_index, {}), scopenum, param_index) newcalls.append(newcallspec) diff --git a/testing/python/metafunc.py b/testing/python/metafunc.py index 7a6d1eebf..215e18b03 100644 --- a/testing/python/metafunc.py +++ b/testing/python/metafunc.py @@ -220,6 +220,46 @@ class TestMetafunc: assert metafunc._calls[0].params == dict(x=1,y=2, unnamed=1) assert metafunc._calls[1].params == dict(x=1,y=3, unnamed=1) + def test_parametrize_indirect_list(self): + def func(x, y): pass + metafunc = self.Metafunc(func) + metafunc.parametrize('x, y', [('a', 'b')], indirect=['x']) + assert metafunc._calls[0].funcargs == dict(y='b') + assert metafunc._calls[0].params == dict(x='a') + + def test_parametrize_indirect_list_all(self): + def func(x, y): pass + metafunc = self.Metafunc(func) + metafunc.parametrize('x, y', [('a', 'b')], indirect=['x', 'y']) + assert metafunc._calls[0].funcargs == {} + assert metafunc._calls[0].params == dict(x='a', y='b') + + def test_parametrize_indirect_list_empty(self): + def func(x, y): pass + metafunc = self.Metafunc(func) + metafunc.parametrize('x, y', [('a', 'b')], indirect=[]) + assert metafunc._calls[0].funcargs == dict(x='a', y='b') + assert metafunc._calls[0].params == {} + + def test_parametrize_indirect_list_functional(self, testdir): + testdir.makepyfile(""" + def pytest_generate_tests(metafunc): + metafunc.parametrize('x, y', [('a', 'b')], indirect=['x']) + def pytest_funcarg__x(request): + return request.param * 3 + def pytest_funcarg__y(request): + return request.param * 2 + + def test_simple(x,y): + assert len(x) == 3 + assert len(y) == 1 + """) + result = testdir.runpytest("-v") + result.stdout.fnmatch_lines([ + "*test_simple*a-b*", + "*1 passed*", + ]) + def test_addcalls_and_parametrize_indirect(self): def func(x, y): pass metafunc = self.Metafunc(func)