Skip to content

Commit 40fa7b2

Browse files
committed
Merge branch 'master' of git://github.com/Elizaveta239/pytest into Elizaveta239-master
Fixed merge conflict in CHANGELOG
2 parents 9ae40e3 + f7bacd1 commit 40fa7b2

File tree

5 files changed

+125
-7
lines changed

5 files changed

+125
-7
lines changed

AUTHORS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ Daniel Nuri
2626
Dave Hunt
2727
David Mohr
2828
Eduardo Schettino
29+
Elizaveta Shashkova
2930
Eric Siegerman
3031
Florian Bruhin
3132
Edison Gustavo Muenz

CHANGELOG

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,10 @@
123123
- fix issue890: changed extension of all documentation files from ``txt`` to
124124
``rst``. Thanks to Abhijeet for the PR.
125125

126+
- fix issue714: add ability to apply indirect=True parameter on particular argnames.
127+
Thanks Elizaveta239.
128+
129+
126130
2.7.3 (compared to 2.7.2)
127131
-----------------------------
128132

_pytest/python.py

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -779,11 +779,12 @@ def getparam(self, name):
779779
def id(self):
780780
return "-".join(map(str, filter(None, self._idlist)))
781781

782-
def setmulti(self, valtype, argnames, valset, id, keywords, scopenum,
782+
def setmulti(self, valtypes, argnames, valset, id, keywords, scopenum,
783783
param_index):
784784
for arg,val in zip(argnames, valset):
785785
self._checkargnotcontained(arg)
786-
getattr(self, valtype)[arg] = val
786+
valtype_for_arg = valtypes[arg]
787+
getattr(self, valtype_for_arg)[arg] = val
787788
self.indices[arg] = param_index
788789
self._arg2scopenum[arg] = scopenum
789790
if val is _notexists:
@@ -850,7 +851,7 @@ def parametrize(self, argnames, argvalues, indirect=False, ids=None,
850851
""" Add new invocations to the underlying test function using the list
851852
of argvalues for the given argnames. Parametrization is performed
852853
during the collection phase. If you need to setup expensive resources
853-
see about setting indirect=True to do it rather at test setup time.
854+
see about setting indirect to do it rather at test setup time.
854855
855856
:arg argnames: a comma-separated string denoting one or more argument
856857
names, or a list/tuple of argument strings.
@@ -862,7 +863,9 @@ def parametrize(self, argnames, argvalues, indirect=False, ids=None,
862863
where each tuple-element specifies a value for its respective
863864
argname.
864865
865-
:arg indirect: if True each argvalue corresponding to an argname will
866+
:arg indirect: The list of argnames or boolean. A list of arguments'
867+
names (subset of argnames). If True the list contains all names from
868+
the argnames. Each argvalue corresponding to an argname in this list will
866869
be passed as request.param to its respective argname fixture
867870
function so that it can perform more expensive setups during the
868871
setup phase of a test rather than at collection time.
@@ -907,13 +910,23 @@ def parametrize(self, argnames, argvalues, indirect=False, ids=None,
907910
if scope is None:
908911
scope = "function"
909912
scopenum = scopes.index(scope)
910-
if not indirect:
913+
valtypes = {}
914+
if indirect is True:
915+
valtypes = dict.fromkeys(argnames, "params")
916+
elif indirect is False:
917+
valtypes = dict.fromkeys(argnames, "funcargs")
911918
#XXX should we also check for the opposite case?
912919
for arg in argnames:
913920
if arg not in self.fixturenames:
914921
raise ValueError("%r uses no fixture %r" %(
915922
self.function, arg))
916-
valtype = indirect and "params" or "funcargs"
923+
elif isinstance(indirect, (tuple, list)):
924+
valtypes = dict.fromkeys(argnames, "funcargs")
925+
for arg in indirect:
926+
if arg not in argnames:
927+
raise ValueError("indirect given to %r: fixture %r doesn't exist" %(
928+
self.function, arg))
929+
valtypes[arg] = "params"
917930
idfn = None
918931
if callable(ids):
919932
idfn = ids
@@ -928,7 +941,7 @@ def parametrize(self, argnames, argvalues, indirect=False, ids=None,
928941
for param_index, valset in enumerate(argvalues):
929942
assert len(valset) == len(argnames)
930943
newcallspec = callspec.copy(self)
931-
newcallspec.setmulti(valtype, argnames, valset, ids[param_index],
944+
newcallspec.setmulti(valtypes, argnames, valset, ids[param_index],
932945
newkeywords.get(param_index, {}), scopenum,
933946
param_index)
934947
newcalls.append(newcallspec)

doc/en/example/parametrize.rst

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,46 @@ The first invocation with ``db == "DB1"`` passed while the second with ``db == "
280280

281281
.. regendoc:wipe
282282
283+
Apply indirect on particular arguments
284+
---------------------------------------------------
285+
286+
Very often parametrization uses more than one argument name. There is opportunity to apply ``indirect``
287+
parameter on particular arguments. It can be done by passing list or tuple of
288+
arguments' names to ``indirect``. In the example below there is a function ``test_indirect`` which uses
289+
two fixtures: ``x`` and ``y``. Here we give to indirect the list, which contains the name of the
290+
fixture ``x``. The indirect parameter will be applied to this argument only, and the value ``a``
291+
will be passed to respective fixture function.
292+
293+
# content of test_indirect_list.py
294+
295+
import pytest
296+
@pytest.fixture(scope='function')
297+
def x(request):
298+
return request.param * 3
299+
300+
@pytest.fixture(scope='function')
301+
def y(request):
302+
return request.param * 2
303+
304+
@pytest.mark.parametrize('x, y', [('a', 'b')], indirect=['x'])
305+
def test_indirect(x,y):
306+
assert x == 'aaa'
307+
assert y == 'b'
308+
309+
The result of this test will be successful:
310+
311+
$ py.test test_indirect_list.py --collect-only
312+
============================= test session starts ==============================
313+
platform linux2 -- Python 2.7.3, pytest-2.8.0.dev4, py-1.4.30, pluggy-0.3.0
314+
rootdir: /home/elizabeth/work/pytest, inifile: tox.ini
315+
collected 1 items
316+
<Module 'testing/test_argnames.py'>
317+
<Function 'test_simple[a-b]'>
318+
319+
=============================== in 0.02 seconds ===============================
320+
321+
.. regendoc:wipe
322+
283323
Parametrizing test methods through per-class configuration
284324
--------------------------------------------------------------
285325

testing/python/metafunc.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@ def func(x, y): pass
208208
assert metafunc._calls[0].id == "0-2"
209209
assert metafunc._calls[1].id == "0-3"
210210

211+
@pytest.mark.issue714
211212
def test_parametrize_indirect(self):
212213
def func(x, y): pass
213214
metafunc = self.Metafunc(func)
@@ -220,6 +221,65 @@ def func(x, y): pass
220221
assert metafunc._calls[0].params == dict(x=1,y=2, unnamed=1)
221222
assert metafunc._calls[1].params == dict(x=1,y=3, unnamed=1)
222223

224+
@pytest.mark.issue714
225+
def test_parametrize_indirect_list(self):
226+
def func(x, y): pass
227+
metafunc = self.Metafunc(func)
228+
metafunc.parametrize('x, y', [('a', 'b')], indirect=['x'])
229+
assert metafunc._calls[0].funcargs == dict(y='b')
230+
assert metafunc._calls[0].params == dict(x='a')
231+
232+
@pytest.mark.issue714
233+
def test_parametrize_indirect_list_all(self):
234+
def func(x, y): pass
235+
metafunc = self.Metafunc(func)
236+
metafunc.parametrize('x, y', [('a', 'b')], indirect=['x', 'y'])
237+
assert metafunc._calls[0].funcargs == {}
238+
assert metafunc._calls[0].params == dict(x='a', y='b')
239+
240+
@pytest.mark.issue714
241+
def test_parametrize_indirect_list_empty(self):
242+
def func(x, y): pass
243+
metafunc = self.Metafunc(func)
244+
metafunc.parametrize('x, y', [('a', 'b')], indirect=[])
245+
assert metafunc._calls[0].funcargs == dict(x='a', y='b')
246+
assert metafunc._calls[0].params == {}
247+
248+
@pytest.mark.issue714
249+
def test_parametrize_indirect_list_functional(self, testdir):
250+
"""
251+
Test parametrization with 'indirect' parameter applied on
252+
particular arguments.
253+
254+
:param testdir: the instance of Testdir class, a temporary
255+
test directory.
256+
"""
257+
testdir.makepyfile("""
258+
import pytest
259+
@pytest.fixture(scope='function')
260+
def x(request):
261+
return request.param * 3
262+
@pytest.fixture(scope='function')
263+
def y(request):
264+
return request.param * 2
265+
@pytest.mark.parametrize('x, y', [('a', 'b')], indirect=['x'])
266+
def test_simple(x,y):
267+
assert len(x) == 3
268+
assert len(y) == 1
269+
""")
270+
result = testdir.runpytest("-v")
271+
result.stdout.fnmatch_lines([
272+
"*test_simple*a-b*",
273+
"*1 passed*",
274+
])
275+
276+
@pytest.mark.issue714
277+
def test_parametrize_indirect_list_error(self, testdir):
278+
def func(x, y): pass
279+
metafunc = self.Metafunc(func)
280+
with pytest.raises(ValueError):
281+
metafunc.parametrize('x, y', [('a', 'b')], indirect=['x', 'z'])
282+
223283
def test_addcalls_and_parametrize_indirect(self):
224284
def func(x, y): pass
225285
metafunc = self.Metafunc(func)

0 commit comments

Comments
 (0)