@@ -5146,7 +5146,6 @@ def test_something(self, request, app):
51465146 result .assert_outcomes (passed = 1 )
51475147
51485148
5149- @pytest .mark .xfail (reason = "not currently handled correctly" )
51505149def test_fixture_closure_with_overrides_and_intermediary (pytester : Pytester ) -> None :
51515150 """Test that an item's static fixture closure properly includes transitive
51525151 dependencies through overridden fixtures (#13773).
@@ -5234,3 +5233,135 @@ def test_something(self, request, app):
52345233 )
52355234 result = pytester .runpytest ("-v" )
52365235 result .assert_outcomes (passed = 1 )
5236+
5237+
5238+ def test_fixture_closure_handles_circular_dependencies (pytester : Pytester ) -> None :
5239+ """Test that getfixtureclosure properly handles circular dependencies.
5240+
5241+ The test will error in the runtest phase due to the fixture loop,
5242+ but the closure computation still completes.
5243+ """
5244+ pytester .makepyfile (
5245+ """
5246+ import pytest
5247+
5248+ # Direct circular dependency.
5249+ @pytest.fixture
5250+ def fix_a(fix_b): pass
5251+
5252+ @pytest.fixture
5253+ def fix_b(fix_a): pass
5254+
5255+ # Indirect circular dependency through multiple fixtures.
5256+ @pytest.fixture
5257+ def fix_x(fix_y): pass
5258+
5259+ @pytest.fixture
5260+ def fix_y(fix_z): pass
5261+
5262+ @pytest.fixture
5263+ def fix_z(fix_x): pass
5264+
5265+ def test_circular_deps(fix_a, fix_x):
5266+ pass
5267+ """
5268+ )
5269+ items , _hookrec = pytester .inline_genitems ()
5270+ assert isinstance (items [0 ], Function )
5271+ assert items [0 ].fixturenames == ["fix_a" , "fix_x" , "fix_b" , "fix_y" , "fix_z" ]
5272+
5273+
5274+ def test_fixture_closure_handles_diamond_dependencies (pytester : Pytester ) -> None :
5275+ """Test that getfixtureclosure properly handles diamond dependencies."""
5276+ pytester .makepyfile (
5277+ """
5278+ import pytest
5279+
5280+ @pytest.fixture
5281+ def db(): pass
5282+
5283+ @pytest.fixture
5284+ def user(db): pass
5285+
5286+ @pytest.fixture
5287+ def session(db): pass
5288+
5289+ @pytest.fixture
5290+ def app(user, session): pass
5291+
5292+ def test_diamond_deps(request, app):
5293+ assert request.node.fixturenames == ["request", "app", "user", "db", "session"]
5294+ assert request.fixturenames == ["request", "app", "user", "db", "session"]
5295+ """
5296+ )
5297+ result = pytester .runpytest ("-v" )
5298+ result .assert_outcomes (passed = 1 )
5299+
5300+
5301+ def test_fixture_closure_with_complex_override_and_shared_deps (
5302+ pytester : Pytester ,
5303+ ) -> None :
5304+ """Test that shared dependencies in override chains are processed only once."""
5305+ pytester .makeconftest (
5306+ """
5307+ import pytest
5308+
5309+ @pytest.fixture
5310+ def db(): pass
5311+
5312+ @pytest.fixture
5313+ def cache(): pass
5314+
5315+ @pytest.fixture
5316+ def settings(): pass
5317+
5318+ @pytest.fixture
5319+ def app(db, cache, settings): pass
5320+ """
5321+ )
5322+ pytester .makepyfile (
5323+ """
5324+ import pytest
5325+
5326+ # Override app, but also directly use cache and settings.
5327+ # This creates multiple paths to the same fixtures.
5328+ @pytest.fixture
5329+ def app(app, cache, settings): pass
5330+
5331+ class TestClass:
5332+ # Another override that uses both app and cache.
5333+ @pytest.fixture
5334+ def app(self, app, cache): pass
5335+
5336+ def test_shared_deps(self, request, app):
5337+ assert request.node.fixturenames == ["request", "app", "db", "cache", "settings"]
5338+ """
5339+ )
5340+ result = pytester .runpytest ("-v" )
5341+ result .assert_outcomes (passed = 1 )
5342+
5343+
5344+ def test_fixture_closure_with_parametrize_ignore (pytester : Pytester ) -> None :
5345+ """Test that getfixtureclosure properly handles parametrization argnames
5346+ which override a fixture."""
5347+ pytester .makepyfile (
5348+ """
5349+ import pytest
5350+
5351+ @pytest.fixture
5352+ def fix1(fix2): pass
5353+
5354+ @pytest.fixture
5355+ def fix2(fix3): pass
5356+
5357+ @pytest.fixture
5358+ def fix3(): pass
5359+
5360+ @pytest.mark.parametrize('fix2', ['2'])
5361+ def test_it(request, fix1):
5362+ assert request.node.fixturenames == ["request", "fix1", "fix2"]
5363+ assert request.fixturenames == ["request", "fix1", "fix2"]
5364+ """
5365+ )
5366+ result = pytester .runpytest ("-v" )
5367+ result .assert_outcomes (passed = 1 )
0 commit comments