diff --git a/AUTHORS b/AUTHORS index 6e2f472fe4a..6252be8ddae 100644 --- a/AUTHORS +++ b/AUTHORS @@ -265,6 +265,7 @@ Vidar T. Fauske Virgil Dupras Vitaly Lashmanov Vlad Dragos +Vladyslav Rachek Volodymyr Piskun Wei Lin Wil Cooley diff --git a/src/_pytest/python.py b/src/_pytest/python.py index d787638c9f1..cc96359f57b 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -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) @@ -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.