Implement unpacking nested pytest.param objects
- Merge nested ids (if parent pytest.param don't have id) - Merge nested marks with parent
This commit is contained in:
parent
1824349f74
commit
ebfdb5fe28
1
AUTHORS
1
AUTHORS
|
@ -334,6 +334,7 @@ Virgil Dupras
|
||||||
Vitaly Lashmanov
|
Vitaly Lashmanov
|
||||||
Vlad Dragos
|
Vlad Dragos
|
||||||
Vlad Radziuk
|
Vlad Radziuk
|
||||||
|
Vladimir Gorkavenko
|
||||||
Vladyslav Rachek
|
Vladyslav Rachek
|
||||||
Volodymyr Piskun
|
Volodymyr Piskun
|
||||||
Wei Lin
|
Wei Lin
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
Now ``pytest`` do unpack nested ``pytest.param`` values and merge their ids and marks.
|
||||||
|
If parent ``pytest.param`` has an id - it rewrites nested ids.
|
||||||
|
For example, now such a construction is possible::
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"a,b,c", [
|
||||||
|
pytest.param(
|
||||||
|
pytest.param(1, id="one", marks=pytest.mark.one),
|
||||||
|
pytest.param(2, id="two", marks=pytest.mark.two),
|
||||||
|
pytest.param(3, id="three", marks=pytest.mark.three),
|
||||||
|
marks=pytest.mark.full
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
Earlier in this case the values ``a, b, c`` in test are a ``ParameterSet`` objects
|
|
@ -1126,9 +1126,21 @@ class Metafunc:
|
||||||
# Create the new calls: if we are parametrize() multiple times (by applying the decorator
|
# Create the new calls: if we are parametrize() multiple times (by applying the decorator
|
||||||
# more than once) then we accumulate those calls generating the cartesian product
|
# more than once) then we accumulate those calls generating the cartesian product
|
||||||
# of all calls.
|
# of all calls.
|
||||||
|
# If we parametrize the test using values that contained nested pytest.param objects,
|
||||||
|
# then we unpack them values and merge marks.
|
||||||
newcalls = []
|
newcalls = []
|
||||||
for callspec in self._calls or [CallSpec2()]:
|
for callspec in self._calls or [CallSpec2()]:
|
||||||
for param_index, (param_id, param_set) in enumerate(zip(ids, parameters)):
|
for param_index, (param_id, param_set) in enumerate(zip(ids, parameters)):
|
||||||
|
normalized_values = []
|
||||||
|
merged_marks = list(param_set.marks) if param_set.marks else []
|
||||||
|
for param_value in param_set.values:
|
||||||
|
if not isinstance(param_value, ParameterSet):
|
||||||
|
normalized_values.append(param_value)
|
||||||
|
else:
|
||||||
|
normalized_values.append(*param_value.values)
|
||||||
|
if param_value.marks:
|
||||||
|
merged_marks.append(*param_value.marks)
|
||||||
|
param_set = ParameterSet(values=tuple(normalized_values), marks=merged_marks, id=None)
|
||||||
newcallspec = callspec.setmulti(
|
newcallspec = callspec.setmulti(
|
||||||
valtypes=arg_values_types,
|
valtypes=arg_values_types,
|
||||||
argnames=argnames,
|
argnames=argnames,
|
||||||
|
@ -1385,10 +1397,15 @@ def _idvalset(
|
||||||
return parameterset.id
|
return parameterset.id
|
||||||
id = None if ids is None or idx >= len(ids) else ids[idx]
|
id = None if ids is None or idx >= len(ids) else ids[idx]
|
||||||
if id is None:
|
if id is None:
|
||||||
this_id = [
|
this_id = []
|
||||||
_idval(val, argname, idx, idfn, nodeid=nodeid, config=config)
|
for val, argname in zip(parameterset.values, argnames):
|
||||||
for val, argname in zip(parameterset.values, argnames)
|
to_get_idval = val
|
||||||
]
|
if isinstance(val, ParameterSet):
|
||||||
|
if val.id:
|
||||||
|
this_id.append(val.id)
|
||||||
|
continue
|
||||||
|
to_get_idval = (val.values[0] if len(val.values) == 1 else val.values)
|
||||||
|
this_id.append(_idval(to_get_idval, argname, idx, idfn, nodeid=nodeid, config=config))
|
||||||
return "-".join(this_id)
|
return "-".join(this_id)
|
||||||
else:
|
else:
|
||||||
return _ascii_escaped_by_config(id, config)
|
return _ascii_escaped_by_config(id, config)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
from typing import List
|
from typing import List, NamedTuple, Tuple, Any
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
|
@ -344,6 +344,61 @@ def test_parametrize_with_module(pytester: Pytester) -> None:
|
||||||
assert passed[0].nodeid.split("::")[-1] == expected_id
|
assert passed[0].nodeid.split("::")[-1] == expected_id
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture()
|
||||||
|
def pytester_internal_test_values():
|
||||||
|
pytest.internal_test_values = [] # type: ignore[attr-defined]
|
||||||
|
yield pytest.internal_test_values # type: ignore[attr-defined]
|
||||||
|
del pytest.internal_test_values # type: ignore[attr-defined]
|
||||||
|
|
||||||
|
|
||||||
|
def test_parametrize_with_nested_paramsets(pytester: Pytester, pytester_internal_test_values: List[Any]) -> None:
|
||||||
|
"""Test parametrize with nested pytest.param objects in value"""
|
||||||
|
case = NamedTuple(
|
||||||
|
"case",
|
||||||
|
[
|
||||||
|
("expected_test_id", str),
|
||||||
|
("expected_value", Tuple[Any, ...]),
|
||||||
|
("expected_marks", Tuple[str, ...]),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
cases = (
|
||||||
|
case("1-nested_2-3", (1, 2, 3), ("parametrize",)),
|
||||||
|
case("one_two_three", (1, 2, 3), ("parametrize",)),
|
||||||
|
case("1-nested_2-tuple_value", (1, 2, (3, 3)), ("parametrize",)),
|
||||||
|
case("1-2-3", (1, 2, 3), ("parametrize", "a", "b", "c", "all",)),
|
||||||
|
)
|
||||||
|
pytester.makepyfile(
|
||||||
|
"""
|
||||||
|
import pytest
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"a, b, c",
|
||||||
|
[
|
||||||
|
(1, pytest.param(2, id="nested_2"), pytest.param(3)),
|
||||||
|
pytest.param(1, pytest.param(2, id="nested_2"), pytest.param(3), id="one_two_three"),
|
||||||
|
(1, pytest.param(2, id="nested_2"), pytest.param((3, 3), id="tuple_value")),
|
||||||
|
pytest.param(
|
||||||
|
pytest.param(1, marks=pytest.mark.a),
|
||||||
|
pytest.param(2, marks=pytest.mark.b),
|
||||||
|
pytest.param(3, marks=pytest.mark.c),
|
||||||
|
marks=pytest.mark.all
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
def test_func(a, b, c):
|
||||||
|
pytest.internal_test_values.append((a, b, c))
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
rec = pytester.inline_run()
|
||||||
|
items = [x.item for x in rec.getcalls("pytest_itemcollected")]
|
||||||
|
passed, _, _ = rec.listoutcomes()
|
||||||
|
for i, case_ in enumerate(cases):
|
||||||
|
markers = {m.name for m in (items[i].iter_markers() or ())}
|
||||||
|
expected_id = "test_func[" + case_.expected_test_id + "]"
|
||||||
|
assert markers == set(case_.expected_marks)
|
||||||
|
assert passed[i].nodeid.split("::")[-1] == expected_id
|
||||||
|
assert pytester_internal_test_values[i] == case_.expected_value
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
("expr", "expected_error"),
|
("expr", "expected_error"),
|
||||||
[
|
[
|
||||||
|
|
Loading…
Reference in New Issue