diff --git a/changelog/12328.bugfix.rst b/changelog/12328.bugfix.rst new file mode 100644 index 000000000..f33442585 --- /dev/null +++ b/changelog/12328.bugfix.rst @@ -0,0 +1 @@ +Fix a regression in pytest 8.0.0 where package-scoped parameterized items were not correctly reordered to minimize setups/teardowns in some cases. diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index 3ce8b3fb4..06da52aed 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -187,7 +187,8 @@ def get_parametrized_fixture_keys( if scope is Scope.Session: scoped_item_path = None elif scope is Scope.Package: - scoped_item_path = item.path + # Package key = module's directory. + scoped_item_path = item.path.parent elif scope is Scope.Module: scoped_item_path = item.path elif scope is Scope.Class: diff --git a/testing/python/fixtures.py b/testing/python/fixtures.py index 15d7c33b2..d54bcc4d4 100644 --- a/testing/python/fixtures.py +++ b/testing/python/fixtures.py @@ -4274,6 +4274,39 @@ class TestScopeOrdering: request = TopRequest(items[0], _ispytest=True) assert request.fixturenames == "s1 p1 m1 m2 c1 f2 f1".split() + def test_parametrized_package_scope_reordering(self, pytester: Pytester) -> None: + """A paramaterized package-scoped fixture correctly reorders items to + minimize setups & teardowns. + + Regression test for #12328. + """ + pytester.makepyfile( + __init__="", + conftest=""" + import pytest + @pytest.fixture(scope="package", params=["a", "b"]) + def fix(request): + return request.param + """, + test_1="def test1(fix): pass", + test_2="def test2(fix): pass", + ) + + result = pytester.runpytest("--setup-plan") + assert result.ret == ExitCode.OK + result.stdout.fnmatch_lines( + [ + " SETUP P fix['a']", + " test_1.py::test1[a] (fixtures used: fix, request)", + " test_2.py::test2[a] (fixtures used: fix, request)", + " TEARDOWN P fix['a']", + " SETUP P fix['b']", + " test_1.py::test1[b] (fixtures used: fix, request)", + " test_2.py::test2[b] (fixtures used: fix, request)", + " TEARDOWN P fix['b']", + ], + ) + def test_multiple_packages(self, pytester: Pytester) -> None: """Complex test involving multiple package fixtures. Make sure teardowns are executed in order.