Merge pull request #6919 from nicoddemus/backport-6914
This commit is contained in:
commit
f506682abe
|
@ -0,0 +1,3 @@
|
||||||
|
Revert the change introduced by `#6330 <https://github.com/pytest-dev/pytest/pull/6330>`_, which required all arguments to ``@pytest.mark.parametrize`` to be explicitly defined in the function signature.
|
||||||
|
|
||||||
|
The intention of the original change was to remove what was expected to be an unintended/surprising behavior, but it turns out many people relied on it, so the restriction has been reverted.
|
|
@ -403,9 +403,6 @@ The result of this test will be successful:
|
||||||
|
|
||||||
.. regendoc:wipe
|
.. regendoc:wipe
|
||||||
|
|
||||||
Note, that each argument in `parametrize` list should be explicitly declared in corresponding
|
|
||||||
python test function or via `indirect`.
|
|
||||||
|
|
||||||
Parametrizing test methods through per-class configuration
|
Parametrizing test methods through per-class configuration
|
||||||
--------------------------------------------------------------
|
--------------------------------------------------------------
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import functools
|
import functools
|
||||||
import inspect
|
import inspect
|
||||||
|
import itertools
|
||||||
import sys
|
import sys
|
||||||
import warnings
|
import warnings
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
@ -1276,8 +1277,10 @@ class FixtureManager:
|
||||||
else:
|
else:
|
||||||
argnames = ()
|
argnames = ()
|
||||||
|
|
||||||
usefixtures = get_use_fixtures_for_node(node)
|
usefixtures = itertools.chain.from_iterable(
|
||||||
initialnames = usefixtures + argnames
|
mark.args for mark in node.iter_markers(name="usefixtures")
|
||||||
|
)
|
||||||
|
initialnames = tuple(usefixtures) + argnames
|
||||||
fm = node.session._fixturemanager
|
fm = node.session._fixturemanager
|
||||||
initialnames, names_closure, arg2fixturedefs = fm.getfixtureclosure(
|
initialnames, names_closure, arg2fixturedefs = fm.getfixtureclosure(
|
||||||
initialnames, node, ignore_args=self._get_direct_parametrize_args(node)
|
initialnames, node, ignore_args=self._get_direct_parametrize_args(node)
|
||||||
|
@ -1474,12 +1477,3 @@ class FixtureManager:
|
||||||
for fixturedef in fixturedefs:
|
for fixturedef in fixturedefs:
|
||||||
if nodes.ischildnode(fixturedef.baseid, nodeid):
|
if nodes.ischildnode(fixturedef.baseid, nodeid):
|
||||||
yield fixturedef
|
yield fixturedef
|
||||||
|
|
||||||
|
|
||||||
def get_use_fixtures_for_node(node) -> Tuple[str, ...]:
|
|
||||||
"""Returns the names of all the usefixtures() marks on the given node"""
|
|
||||||
return tuple(
|
|
||||||
str(name)
|
|
||||||
for mark in node.iter_markers(name="usefixtures")
|
|
||||||
for name in mark.args
|
|
||||||
)
|
|
||||||
|
|
|
@ -936,8 +936,6 @@ class Metafunc:
|
||||||
|
|
||||||
arg_values_types = self._resolve_arg_value_types(argnames, indirect)
|
arg_values_types = self._resolve_arg_value_types(argnames, indirect)
|
||||||
|
|
||||||
self._validate_explicit_parameters(argnames, indirect)
|
|
||||||
|
|
||||||
# Use any already (possibly) generated ids with parametrize Marks.
|
# Use any already (possibly) generated ids with parametrize Marks.
|
||||||
if _param_mark and _param_mark._param_ids_from:
|
if _param_mark and _param_mark._param_ids_from:
|
||||||
generated_ids = _param_mark._param_ids_from._param_ids_generated
|
generated_ids = _param_mark._param_ids_from._param_ids_generated
|
||||||
|
@ -1110,39 +1108,6 @@ class Metafunc:
|
||||||
pytrace=False,
|
pytrace=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
def _validate_explicit_parameters(
|
|
||||||
self,
|
|
||||||
argnames: typing.Sequence[str],
|
|
||||||
indirect: Union[bool, typing.Sequence[str]],
|
|
||||||
) -> None:
|
|
||||||
"""
|
|
||||||
The argnames in *parametrize* should either be declared explicitly via
|
|
||||||
indirect list or in the function signature
|
|
||||||
|
|
||||||
:param List[str] argnames: list of argument names passed to ``parametrize()``.
|
|
||||||
:param indirect: same ``indirect`` parameter of ``parametrize()``.
|
|
||||||
:raise ValueError: if validation fails
|
|
||||||
"""
|
|
||||||
if isinstance(indirect, bool):
|
|
||||||
parametrized_argnames = [] if indirect else argnames
|
|
||||||
else:
|
|
||||||
parametrized_argnames = [arg for arg in argnames if arg not in indirect]
|
|
||||||
|
|
||||||
if not parametrized_argnames:
|
|
||||||
return
|
|
||||||
|
|
||||||
funcargnames = _pytest.compat.getfuncargnames(self.function)
|
|
||||||
usefixtures = fixtures.get_use_fixtures_for_node(self.definition)
|
|
||||||
|
|
||||||
for arg in parametrized_argnames:
|
|
||||||
if arg not in funcargnames and arg not in usefixtures:
|
|
||||||
func_name = self.function.__name__
|
|
||||||
msg = (
|
|
||||||
'In function "{func_name}":\n'
|
|
||||||
'Parameter "{arg}" should be declared explicitly via indirect or in function itself'
|
|
||||||
).format(func_name=func_name, arg=arg)
|
|
||||||
fail(msg, pytrace=False)
|
|
||||||
|
|
||||||
|
|
||||||
def _find_parametrized_scope(argnames, arg2fixturedefs, indirect):
|
def _find_parametrized_scope(argnames, arg2fixturedefs, indirect):
|
||||||
"""Find the most appropriate scope for a parametrized call based on its arguments.
|
"""Find the most appropriate scope for a parametrized call based on its arguments.
|
||||||
|
|
|
@ -463,7 +463,7 @@ class TestFunction:
|
||||||
return '3'
|
return '3'
|
||||||
|
|
||||||
@pytest.mark.parametrize('fix2', ['2'])
|
@pytest.mark.parametrize('fix2', ['2'])
|
||||||
def test_it(fix1, fix2):
|
def test_it(fix1):
|
||||||
assert fix1 == '21'
|
assert fix1 == '21'
|
||||||
assert not fix3_instantiated
|
assert not fix3_instantiated
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -36,9 +36,6 @@ class TestMetafunc:
|
||||||
class DefinitionMock(python.FunctionDefinition):
|
class DefinitionMock(python.FunctionDefinition):
|
||||||
obj = attr.ib()
|
obj = attr.ib()
|
||||||
|
|
||||||
def listchain(self):
|
|
||||||
return []
|
|
||||||
|
|
||||||
names = fixtures.getfuncargnames(func)
|
names = fixtures.getfuncargnames(func)
|
||||||
fixtureinfo = FuncFixtureInfoMock(names) # type: Any
|
fixtureinfo = FuncFixtureInfoMock(names) # type: Any
|
||||||
definition = DefinitionMock._create(func) # type: Any
|
definition = DefinitionMock._create(func) # type: Any
|
||||||
|
@ -1902,51 +1899,3 @@ class TestMarkersWithParametrization:
|
||||||
"*= 6 passed in *",
|
"*= 6 passed in *",
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_parametrize_explicit_parameters_func(self, testdir: Testdir) -> None:
|
|
||||||
testdir.makepyfile(
|
|
||||||
"""
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def fixture(arg):
|
|
||||||
return arg
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("arg", ["baz"])
|
|
||||||
def test_without_arg(fixture):
|
|
||||||
assert "baz" == fixture
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
result = testdir.runpytest()
|
|
||||||
result.assert_outcomes(error=1)
|
|
||||||
result.stdout.fnmatch_lines(
|
|
||||||
[
|
|
||||||
'*In function "test_without_arg"*',
|
|
||||||
'*Parameter "arg" should be declared explicitly via indirect or in function itself*',
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_parametrize_explicit_parameters_method(self, testdir: Testdir) -> None:
|
|
||||||
testdir.makepyfile(
|
|
||||||
"""
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
class Test:
|
|
||||||
@pytest.fixture
|
|
||||||
def test_fixture(self, argument):
|
|
||||||
return argument
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("argument", ["foobar"])
|
|
||||||
def test_without_argument(self, test_fixture):
|
|
||||||
assert "foobar" == test_fixture
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
result = testdir.runpytest()
|
|
||||||
result.assert_outcomes(error=1)
|
|
||||||
result.stdout.fnmatch_lines(
|
|
||||||
[
|
|
||||||
'*In function "test_without_argument"*',
|
|
||||||
'*Parameter "argument" should be declared explicitly via indirect or in function itself*',
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
Loading…
Reference in New Issue