From 89086a13f7f6faafd24da236b2a6e88468c3f17f Mon Sep 17 00:00:00 2001 From: Vladyslav Rachek Date: Sun, 8 Dec 2019 11:17:04 +0100 Subject: [PATCH] First approach to enforcing setting 'indirect' on dependent fixtures This is a try to solve #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? --- AUTHORS | 1 + src/_pytest/python.py | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+) 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..26fee20498c 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,32 @@ 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 ( + type(indirect) is not bool and 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.