Merge pull request #6919 from nicoddemus/backport-6914

This commit is contained in:
Bruno Oliveira 2020-05-16 12:42:15 -03:00 committed by GitHub
commit f506682abe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 9 additions and 101 deletions

View File

@ -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.

View File

@ -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
-------------------------------------------------------------- --------------------------------------------------------------

View File

@ -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
)

View File

@ -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.

View File

@ -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
""" """

View File

@ -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*',
]
)