Skip to content

Commit 3495438

Browse files
author
Sylvain MARIE
committed
@pytest_fixture_plus now correctly honors parameter id and marks overriden at single parameter level using pytest.param. Fixed #30
Updated tests accordingly New utility function `extract_parameterset_info` in `common.py`.
1 parent d3cc88d commit 3495438

File tree

3 files changed

+102
-48
lines changed

3 files changed

+102
-48
lines changed

pytest_cases/common.py

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ class _ParametrizationMark:
2626

2727
def __init__(self, mark):
2828
bound = get_parametrize_signature().bind(*mark.args, **mark.kwargs)
29-
self.param_names = bound.arguments['argnames'].split(',')
29+
self.param_names = bound.arguments['argnames'].replace(' ', '').split(',')
3030
self.param_values = bound.arguments['argvalues']
3131
try:
3232
bound.apply_defaults()
@@ -116,13 +116,13 @@ def get_test_ids_from_param_values(param_names,
116116
117117
:param param_names:
118118
:param param_values:
119-
:return:
119+
:return: a list of param ids
120120
"""
121121
nb_params = len(param_names)
122122
if nb_params == 0:
123123
raise ValueError("empty list provided")
124124
elif nb_params == 1:
125-
paramids = tuple(str(v) for v in param_values)
125+
paramids = list(str(v) for v in param_values)
126126
else:
127127
paramids = []
128128
for vv in param_values:
@@ -134,6 +134,41 @@ def get_test_ids_from_param_values(param_names,
134134

135135

136136
# ---- ParameterSet api ---
137+
def extract_parameterset_info(pnames, pmark):
138+
"""
139+
140+
:param pnames: the names in this parameterset
141+
:param pmark: the parametrization mark (a _ParametrizationMark)
142+
:return:
143+
"""
144+
_pids = []
145+
_pmarks = []
146+
_pvalues = []
147+
for v in pmark.param_values:
148+
if is_marked_parameter_value(v):
149+
# --id
150+
id = get_marked_parameter_id(v)
151+
_pids.append(id)
152+
# --marks
153+
marks = get_marked_parameter_marks(v)
154+
_pmarks.append(marks) # note: there might be several
155+
# --value(a tuple if this is a tuple parameter)
156+
vals = get_marked_parameter_values(v)
157+
if len(vals) != len(pnames):
158+
raise ValueError("Internal error - unsupported pytest parametrization+mark combination. Please "
159+
"report this issue")
160+
if len(vals) == 1:
161+
_pvalues.append(vals[0])
162+
else:
163+
_pvalues.append(vals)
164+
else:
165+
_pids.append(None)
166+
_pmarks.append(None)
167+
_pvalues.append(v)
168+
169+
return _pids, _pmarks, _pvalues
170+
171+
137172
try: # pytest 3.x+
138173
from _pytest.mark import ParameterSet
139174
def is_marked_parameter_value(v):
@@ -145,6 +180,9 @@ def get_marked_parameter_marks(v):
145180
def get_marked_parameter_values(v):
146181
return v.values
147182

183+
def get_marked_parameter_id(v):
184+
return v.id
185+
148186
except ImportError: # pytest 2.x
149187
from _pytest.mark import MarkDecorator
150188

@@ -157,6 +195,9 @@ def get_marked_parameter_marks(v):
157195
def get_marked_parameter_values(v):
158196
return v.args[1:]
159197

198+
def get_marked_parameter_id(v):
199+
return v.kwargs.get('id', None)
200+
160201

161202
# ---- tools to reapply marks on test parameter values, whatever the pytest version ----
162203

pytest_cases/main.py

Lines changed: 30 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,7 @@
4242

4343
from pytest_cases.case_funcs import _GENERATOR_FIELD, CASE_TAGS_FIELD
4444
from pytest_cases.common import yield_fixture, get_pytest_parametrize_marks, get_test_ids_from_param_values, \
45-
is_marked_parameter_value, get_marked_parameter_marks, get_marked_parameter_values, make_marked_parameter_value, \
46-
get_pytest_marks_on_function
45+
make_marked_parameter_value, get_pytest_marks_on_function, extract_parameterset_info
4746

4847

4948
class CaseDataGetter(six.with_metaclass(ABCMeta)):
@@ -271,58 +270,49 @@ def pytest_fixture_plus(scope="function",
271270
return fix_creator(scope=scope, autouse=autouse, **kwargs)(fixture_func)
272271

273272
# (2) create the huge "param" containing all params combined
274-
# --loop
273+
# --loop (use the same order to get it right)
275274
params_names_or_name_combinations = []
276275
params_values = []
277276
params_ids = []
278277
params_marks = []
279-
# -- use same order to get it right
280-
for m in parametrizer_marks:
281-
# check what the mark specifies in terms of parameters
282-
if len(m.param_names) < 1:
278+
for pmark in parametrizer_marks:
279+
# check number of parameter names in this parameterset
280+
if len(pmark.param_names) < 1:
283281
raise ValueError("Fixture function '%s' decorated with '@pytest_fixture_plus' has an empty parameter "
284282
"name in a @pytest.mark.parametrize mark")
285283

284+
# remember
285+
params_names_or_name_combinations.append(pmark.param_names)
286+
287+
# extract all parameters that have a specific configuration (pytest.param())
288+
_pids, _pmarks, _pvalues = extract_parameterset_info(pmark.param_names, pmark)
289+
290+
# Create the proper id for each test
291+
if pmark.param_ids is not None:
292+
# overridden at global pytest.mark.parametrize level - this takes precedence.
293+
try: # an explicit list of ids ?
294+
paramids = list(pmark.param_ids)
295+
except TypeError: # a callable to apply on the values
296+
paramids = list(pmark.param_ids(v) for v in _pvalues)
286297
else:
287-
# fix bug: pytest does not strip the param names when they come from a coma-separated "arg1, arg2"
288-
_pnames = tuple(name.strip() for name in m.param_names)
289-
290-
# remember
291-
params_names_or_name_combinations.append(_pnames)
292-
_pvalues = []
293-
_pmarks = []
294-
for v in m.param_values:
295-
if is_marked_parameter_value(v):
296-
marks = get_marked_parameter_marks(v)
297-
vals = get_marked_parameter_values(v)
298-
if len(vals) != len(_pnames):
299-
raise ValueError("Internal error - unsupported pytest parametrization+mark combination. Please "
300-
"report this issue")
301-
_pmarks.append(marks) # there might be several
302-
if len(vals) == 1:
303-
_pvalues.append(vals[0])
304-
else:
305-
_pvalues.append(vals)
306-
else:
307-
_pmarks.append(None)
308-
_pvalues.append(v)
309-
params_values.append(tuple(_pvalues))
310-
params_marks.append(tuple(_pmarks))
311-
if m.param_ids is not None:
312-
try:
313-
paramids = tuple(m.param_ids)
314-
except TypeError:
315-
paramids = tuple(m.param_ids(v) for v in _pvalues)
316-
else:
317-
paramids = get_test_ids_from_param_values(_pnames, _pvalues)
318-
params_ids.append(paramids)
298+
# default: values-based...
299+
paramids = get_test_ids_from_param_values(pmark.param_names, _pvalues)
300+
# ...but local pytest.param takes precedence
301+
for i, _id in enumerate(_pids):
302+
if _id is not None:
303+
paramids[i] = _id
304+
305+
# Finally store the ids, marks, and values for this parameterset
306+
params_ids.append(paramids)
307+
params_marks.append(tuple(_pmarks))
308+
params_values.append(tuple(_pvalues))
319309

320310
# (3) generate the ids and values, possibly reapplying marks
321311
if len(params_names_or_name_combinations) == 1:
322312
# we can simplify - that will be more readable
323-
final_values = list(params_values[0])
324313
final_ids = params_ids[0]
325314
final_marks = params_marks[0]
315+
final_values = list(params_values[0])
326316

327317
# reapply the marks
328318
for i, marks in enumerate(final_marks):

pytest_cases/tests/simple/test_fixtures_params.py

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
if LooseVersion(pytest.__version__) >= LooseVersion('3.0.0'):
88
pytest_param = pytest.param
99
else:
10-
def pytest_param(*args):
10+
def pytest_param(*args, **kwargs):
1111
return args
1212

1313

@@ -27,22 +27,45 @@ def test_one(myfix):
2727
@pytest_fixture_plus
2828
@pytest.mark.parametrize("arg1, arg2", [
2929
(1, 2),
30-
pytest_param(3, 4),
30+
pytest_param(3, 4, id="p_a"),
31+
pytest_param(5, 6, id="skipped", marks=pytest.mark.skip)
3132
])
3233
def myfix2(arg1, arg2):
3334
return arg1, arg2
3435

3536

3637
def test_two(myfix2):
37-
assert myfix2 in {(1, 2), (3, 4)}
38+
assert myfix2 in {(1, 2), (3, 4), (5, 6)}
39+
print(myfix2)
40+
41+
42+
@pytest_fixture_plus
43+
@pytest.mark.parametrize("arg1, arg2", [
44+
pytest_param(5, 6, id="ignored_id")
45+
], ids=['a'])
46+
def myfix3(arg1, arg2):
47+
return arg1, arg2
48+
49+
50+
def test_two(myfix2, myfix3):
51+
assert myfix2 in {(1, 2), (3, 4), (5, 6)}
3852
print(myfix2)
3953

4054

4155
def test_synthesis(module_results_dct):
4256
"""Use pytest-harvest to check that the list of executed tests is correct """
57+
58+
if LooseVersion(pytest.__version__) >= LooseVersion('3.0.0'):
59+
id_of_last_tests = ['p_a', 'skipped']
60+
extra_test = []
61+
else:
62+
id_of_last_tests = ['3-4', '5-6']
63+
extra_test = ['test_two[%s-a]' % id_of_last_tests[1]]
64+
4365
assert list(module_results_dct) == ['test_one[one-one]',
4466
'test_one[one-two]',
4567
'test_one[two-one]',
4668
'test_one[two-two]',
47-
'test_two[1-2]',
48-
'test_two[3-4]']
69+
'test_two[1-2-a]',
70+
'test_two[%s-a]' % id_of_last_tests[0],
71+
] + extra_test

0 commit comments

Comments
 (0)