Skip to content

Commit af00c4a

Browse files
committed
fixtures: fix fixture closure calculation to properly consider overridden fixtures
Fix #13773.
1 parent 9498a82 commit af00c4a

File tree

3 files changed

+57
-3
lines changed

3 files changed

+57
-3
lines changed

changelog/13773.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixed the static fixture closure calculation to properly consider transitive dependencies requested by overridden fixtures.

src/_pytest/fixtures.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1645,9 +1645,23 @@ def getfixtureclosure(
16451645
fixturedefs = self.getfixturedefs(argname, parentnode)
16461646
if fixturedefs:
16471647
arg2fixturedefs[argname] = fixturedefs
1648-
for arg in fixturedefs[-1].argnames:
1649-
if arg not in fixturenames_closure:
1650-
fixturenames_closure.append(arg)
1648+
1649+
# Add dependencies from this fixture.
1650+
# If it overrides another fixture with the same name (and requests
1651+
# it), also add dependencies from the overridden fixtures in the
1652+
# chain. See also similar dealing in _get_active_fixturedef().
1653+
index = -1
1654+
while -index <= len(fixturedefs):
1655+
for arg in fixturedefs[index].argnames:
1656+
if arg not in fixturenames_closure:
1657+
fixturenames_closure.append(arg)
1658+
if (
1659+
len(fixturedefs) > 1
1660+
and argname in fixturedefs[index].argnames
1661+
):
1662+
index -= 1
1663+
else:
1664+
break
16511665

16521666
def sort_by_scope(arg_name: str) -> Scope:
16531667
try:

testing/python/fixtures.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5069,3 +5069,42 @@ def test_method(self, /, fix):
50695069
)
50705070
result = pytester.runpytest()
50715071
result.assert_outcomes(passed=1)
5072+
5073+
5074+
def test_fixture_closure_with_overrides(pytester: Pytester) -> None:
5075+
"""Test that an item's static fixture closure properly includes transitive
5076+
dependencies through overridden fixtures (#13773)."""
5077+
pytester.makeconftest(
5078+
"""
5079+
import pytest
5080+
5081+
@pytest.fixture
5082+
def db(): pass
5083+
5084+
@pytest.fixture
5085+
def app(db): pass
5086+
"""
5087+
)
5088+
pytester.makepyfile(
5089+
"""
5090+
import pytest
5091+
5092+
# Overrides conftest-level `app` and requests it.
5093+
@pytest.fixture
5094+
def app(app): pass
5095+
5096+
class TestClass:
5097+
# Overrides module-level `app` and requests it.
5098+
@pytest.fixture
5099+
def app(self, app): pass
5100+
5101+
def test_something(self, request, app):
5102+
# Both dynamic and static fixture closures should include 'db'.
5103+
assert 'db' in request.fixturenames
5104+
assert 'db' in request.node.fixturenames
5105+
# No dynamic dependencies, should be equal.
5106+
assert set(request.fixturenames) == set(request.node.fixturenames)
5107+
"""
5108+
)
5109+
result = pytester.runpytest("-v")
5110+
result.assert_outcomes(passed=1)

0 commit comments

Comments
 (0)