Skip to content

Commit

Permalink
First approach to enforcing setting 'indirect' on dependent fixtures
Browse files Browse the repository at this point in the history
This is a try to solve pytest-dev#5712 using following logic:
(1) If there exists a fixture F which uses say parameter A
(2) And there exist a test T in which parameter named A is present
    in pytest.mark.parametrize() arglist
(3) Then A should be marked explicitly as indirect (via indirect=True or indirect=['A',...])
    And if it isn't, we raise ValueError

TODO: as of now, ValueError occurs during collection phase. Maybe we
want it to appear during test phase?
  • Loading branch information
erheron committed Dec 8, 2019
1 parent 9d90093 commit ad99996
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 0 deletions.
1 change: 1 addition & 0 deletions AUTHORS
Expand Up @@ -265,6 +265,7 @@ Vidar T. Fauske
Virgil Dupras
Vitaly Lashmanov
Vlad Dragos
Vladyslav Rachek
Volodymyr Piskun
Wei Lin
Wil Cooley
Expand Down
25 changes: 25 additions & 0 deletions src/_pytest/python.py
Expand Up @@ -974,6 +974,7 @@ def parametrize(self, argnames, argvalues, indirect=False, ids=None, scope=None)
scope = _find_parametrized_scope(argnames, self._arg2fixturedefs, indirect)

self._validate_if_using_arg_names(argnames, indirect)
self._validate_dependent_fixtures(argnames, indirect)

arg_values_types = self._resolve_arg_value_types(argnames, indirect)

Expand Down Expand Up @@ -1096,6 +1097,30 @@ def _validate_if_using_arg_names(self, argnames, indirect):
pytrace=False,
)

def _validate_dependent_fixtures(self, argnames, indirect):
"""
If there exists a fixture F1 which uses fixture F2
and in some test T fixture F2 is overriden via pytest.mark.parametrize
then F2 should be marked as indirect, or we raise an explicit error
:param List[str] argnames: list of argument names passed to ``parametrize()``.
:param indirect: same ``indirect`` parameter of ``parametrize()``.
:raise ValueError: if validation fails
"""
func_name = self.function.__name__
for arg in argnames:
if (type(indirect) is bool and indirect is False) or (arg not in indirect):
for f, f_def_list in self._arg2fixturedefs.items():
for f_def in f_def_list:
if (
arg in f_def.argnames
): # if f uses a as an argument, then a should be marked as indirect
raise ValueError(
f'In function "{func_name}":\n'
f'Parameter "{arg}" should be explicitly marked as indirect\n'
f'Because it\'s used by fixture: "{f}"'
)


def _find_parametrized_scope(argnames, arg2fixturedefs, indirect):
"""Find the most appropriate scope for a parametrized call based on its arguments.
Expand Down

0 comments on commit ad99996

Please sign in to comment.