Repository navigation
function-scoped fixture run before session-scoped fixture #5303
Description
Activity
From what I was able to understand from the code changed in #3306, the problem is that in
getfixtureclosurethe fixtures are referred to only by name, not bybaseid, therefore we don't have any way to differentiate between the two fixtures namedfixture_threeIt looks like in order to solve it, a significant amount of code would need to be modified to refer to fixtures with a more fully-qualified name. @RonnyPfannschmidt @nicoddemus based on the git blame you seem to be the ones with the most intimate knowledge of this code. Any insight?
nope, sorry
- addedtopic: fixturesanything involving fixtures directly or indirectlyanything involving fixtures directly or indirectly
on Jun 5, 2019 I would always want a redefined fixture in a subdirectory to overwrite the one from the parent directory. If, for example, a test in
subdirectory/test_it.pywanted to use a fixture calledfixture_three, which fixture would it get?
If I want to call a fixture and modify its output I should create a fixture with a new name (otherwise the order of my fixture definitions insubdirectory/conftest.pywould determine which fixtures were available as I defined new ones? (e.g. afixture_zerodefined at the top of the file would have access to the session-scopedfixture_threefrom the root directory, but afixture_fourdefined at the bottom wouldn't?)Noticed this too while reviewing #11243. As @brianmaissy said, the problem is here:
pytest/src/_pytest/fixtures.py
Line 1558 in 9c8937b
merge(fixturedefs[-1].argnames) Just using
-1means it doesn't work right for fixture overrides.A simpler reproduction is:
import pytest @pytest.fixture(scope="module") def whoops(): print("WHOOPS") @pytest.fixture(scope="module") def fix(whoops): print("MOD") class Test: @pytest.fixture(scope="function") def fix(self, fix): print("FUNC") def test(self, fix): pass
The
testruns all fixtures, so the expected fixture closure is["fix", "whoops"], but the actual is["fix"]. This is becausegetfixtureclosureonly looks atfixturedefs[-1], i.e.fixturedefs[1], which is the function-scopedfix, and ignores the class-scopedfixwhich is atfixturedefs[0].I guess
getfixtureclosureneeds to handle this properly like_getnextfixturedefdoes. Specifically, iffixturedefs[i](transitively) requests its own name, we should then includefixturedefs[i - 1]in the closure. But I haven't looked too closely yet.- addedtype: bugproblem that needs to be addressedproblem that needs to be addressed
on Sep 4, 2023 I reported a bug in #11350 where this results in a hard error instead of just a wrong ordering
Closing as resolved: the static fixture closure computation now correctly handles override chains via a DFS-based
traverse_fixture_closure(#13774, #13789, fixing the duplicate report #13773), so session-scopedfixture_oneis now set up before the function-scoped autouse fixture as expected. Verified by reproducing the exact repro from this issue on current pytest main.
This is a possible regression of #2405, or possibly a previously unconsidered edge case.
I have a case where a conftest in a subdirectory overrides a fixture with the same name from the enclosing directory's conftest, and this fact causes the fixtures to be set up in the wrong order (a function-scoped fixture is run before a session-scoped fixture).
Here's the example:
The root conftest:
The subdirectory conftest:
test_it.py:
The output:
As you can see, fixture_two (the autouse function-scoped fixture) is run before fixture_one, which is the session-scoped dependency of the original fixture_three (and an indirect dependency of the overridden fixture_three).
Renaming the second fixture_three solves the problem.