From 143c703b3f071fc7cd9716490af2cdad58378656 Mon Sep 17 00:00:00 2001 From: Andres Date: Mon, 11 Mar 2024 13:56:17 -0300 Subject: [PATCH] fix overriden/extended fixtures --- src/_pytest/fixtures.py | 17 ++++++++++++++++- testing/test_mark.py | 29 +++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index 48429a023..538a0ee5e 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -1505,6 +1505,20 @@ class FixtureManager: # to re-discover fixturedefs again for each fixturename # (discovering matching fixtures for a given name/node is expensive). + def dependent_fixtures_argnames( + fixture_defs: Sequence[FixtureDef[Any]], + ) -> List[str]: + last_fixture = fixture_defs[-1] + # Initialize with the argnames of the last fixture + dependent_argnames = list(last_fixture.argnames) + for arg in fixture_defs: + if arg.argname in last_fixture.argnames: + # Add new argument names maintaining order and avoiding duplicates + for argname in arg.argnames: + if argname not in dependent_argnames: + dependent_argnames.append(argname) + return dependent_argnames + fixturenames_closure = list(initialnames) arg2fixturedefs: Dict[str, Sequence[FixtureDef[Any]]] = {} @@ -1519,7 +1533,8 @@ class FixtureManager: fixturedefs = self.getfixturedefs(argname, parentnode) if fixturedefs: arg2fixturedefs[argname] = fixturedefs - for arg in fixturedefs[-1].argnames: + argnames = dependent_fixtures_argnames(fixturedefs) + for arg in argnames: if arg not in fixturenames_closure: fixturenames_closure.append(arg) diff --git a/testing/test_mark.py b/testing/test_mark.py index 2896afa45..bbce30e8d 100644 --- a/testing/test_mark.py +++ b/testing/test_mark.py @@ -447,6 +447,35 @@ def test_parametrized_with_kwargs(pytester: Pytester) -> None: assert result.ret == 0 +def test_parametrize_overriden_extended_fixture(pytester: Pytester) -> None: + """Overriden fixtures must pass over dependend fixtures for parameterization (#12091)""" + py_file = pytester.makepyfile( + """\ + import pytest + + @pytest.fixture + def param() -> int: + return 1 + + @pytest.fixture + def main(param: int) -> int: + return sum(range(param + 1)) + + + class TestFoo: + @pytest.fixture + def main(self, main: int) -> int: + return main + + @pytest.mark.parametrize("param", [2]) + def test_foo(self, main: int) -> None: + assert main == 3 + """ + ) + result = pytester.runpytest(py_file) + assert result.ret == 0 + + def test_parametrize_iterator(pytester: Pytester) -> None: """`parametrize` should work with generators (#5354).""" py_file = pytester.makepyfile(