Skip to content

Commit 2ef9822

Browse files
committed
Consider usefixtures marker for enforcing params
1 parent 25d8b5a commit 2ef9822

File tree

3 files changed

+25
-11
lines changed

3 files changed

+25
-11
lines changed

src/_pytest/fixtures.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1272,10 +1272,8 @@ def getfixtureinfo(self, node, func, cls, funcargs=True):
12721272
else:
12731273
argnames = ()
12741274

1275-
usefixtures = itertools.chain.from_iterable(
1276-
mark.args for mark in node.iter_markers(name="usefixtures")
1277-
)
1278-
initialnames = tuple(usefixtures) + argnames
1275+
usefixtures = get_use_fixtures_for_node(node)
1276+
initialnames = usefixtures + argnames
12791277
fm = node.session._fixturemanager
12801278
initialnames, names_closure, arg2fixturedefs = fm.getfixtureclosure(
12811279
initialnames, node, ignore_args=self._get_direct_parametrize_args(node)
@@ -1472,3 +1470,13 @@ def _matchfactories(self, fixturedefs, nodeid):
14721470
for fixturedef in fixturedefs:
14731471
if nodes.ischildnode(fixturedef.baseid, nodeid):
14741472
yield fixturedef
1473+
1474+
1475+
def get_use_fixtures_for_node(node) -> Tuple[str, ...]:
1476+
"""Returns the names of all the usefixtures() marks on the given node"""
1477+
return tuple(
1478+
str(x)
1479+
for x in itertools.chain.from_iterable(
1480+
mark.args for mark in node.iter_markers(name="usefixtures")
1481+
)
1482+
)

src/_pytest/python.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1162,7 +1162,6 @@ def _validate_explicit_parameters(self, argnames, indirect):
11621162
:param indirect: same ``indirect`` parameter of ``parametrize()``.
11631163
:raise ValueError: if validation fails
11641164
"""
1165-
func_name = self.function.__name__
11661165
if isinstance(indirect, bool) and indirect is True:
11671166
return
11681167
parametrized_argnames = list()
@@ -1173,13 +1172,17 @@ def _validate_explicit_parameters(self, argnames, indirect):
11731172
parametrized_argnames.append(arg)
11741173
elif indirect is False:
11751174
parametrized_argnames = argnames
1175+
1176+
usefixtures = fixtures.get_use_fixtures_for_node(self.definition)
1177+
11761178
for arg in parametrized_argnames:
1177-
if arg not in funcargnames:
1178-
raise ValueError(
1179-
f'In function "{func_name}":\n'
1180-
f'Parameter "{arg}" should be declared explicitly via indirect\n'
1181-
f"or in function itself"
1182-
)
1179+
if arg not in funcargnames and arg not in usefixtures:
1180+
func_name = self.function.__name__
1181+
msg = (
1182+
'In function "{func_name}":\n'
1183+
'Parameter "{arg}" should be declared explicitly via indirect or in function itself'
1184+
).format(func_name=func_name, arg=arg)
1185+
fail(msg, pytrace=False)
11831186

11841187

11851188
def _find_parametrized_scope(argnames, arg2fixturedefs, indirect):

testing/python/metafunc.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ def __init__(self, names):
2828
class DefinitionMock(python.FunctionDefinition):
2929
obj = attr.ib()
3030

31+
def listchain(self):
32+
return []
33+
3134
names = fixtures.getfuncargnames(func)
3235
fixtureinfo = FixtureInfo(names)
3336
definition = DefinitionMock._create(func)

0 commit comments

Comments
 (0)