pytest: Possible Bug in package scoped fixture teardown

Please consider the following diff against PyTest 4.1:

diff --git a/testing/python/fixture.py b/testing/python/fixture.py
index b6692ac9..7b59cef0 100644
--- a/testing/python/fixture.py
+++ b/testing/python/fixture.py
@@ -3792,7 +3792,11 @@ class TestScopeOrdering(object):
             ├── sub1
             │   ├── __init__.py
             │   ├── conftest.py
-            │   └── test_1.py
+            │   ├── test_1.py
+            │   └── sub1_2
+            │       ├── __init__.py
+            │       ├── conftest.py
+            │       └── test_1_2.py
             └── sub2
                 ├── __init__.py
                 ├── conftest.py
@@ -3824,6 +3828,30 @@ class TestScopeOrdering(object):
         """
             )
         )
+        sub1_2 = sub1.mkdir("sub1_2")
+        sub1_2.ensure("__init__.py")
+        sub1_2.join("conftest.py").write(
+            textwrap.dedent(
+                """\
+            import pytest
+            from ... import values
+            @pytest.fixture(scope="package")
+            def fix():
+                values.append("pre-sub1_2")
+                yield values
+                assert values.pop() == "pre-sub1_2"
+        """
+            )
+        )
+        sub1_2.join("test_1_2.py").write(
+            textwrap.dedent(
+                """\
+            from ... import values
+            def test_1_2(fix):
+                assert values == ["pre-sub1_2"]
+        """
+            )
+        )
         sub2 = root.mkdir("sub2")
         sub2.ensure("__init__.py")
         sub2.join("conftest.py").write(
@@ -3849,7 +3877,103 @@ class TestScopeOrdering(object):
             )
         )
         reprec = testdir.inline_run()
-        reprec.assertoutcome(passed=2)
+        reprec.assertoutcome(passed=3)
+
+    def test_multiple_packages_inherit(self, testdir):
+        """Complex test involving multiple package fixtures. Make sure teardowns
+        are executed in order.
+        .
+        └── root
+            ├── __init__.py
+            ├── sub1
+            │   ├── __init__.py
+            │   ├── conftest.py
+            │   ├── test_1.py
+            │   └── sub1_2
+            │       ├── __init__.py
+            │       ├── conftest.py
+            │       └── test_1_2.py
+            └── sub2
+                ├── __init__.py
+                ├── conftest.py
+                └── test_2.py
+        """
+        root = testdir.mkdir("root")
+        root.join("__init__.py").write("values = []")
+        sub1 = root.mkdir("sub1")
+        sub1.ensure("__init__.py")
+        sub1.join("conftest.py").write(
+            textwrap.dedent(
+                """\
+            import pytest
+            from .. import values
+            @pytest.fixture(scope="package")
+            def fix():
+                values.append("pre-sub1")
+                yield values
+                assert values.pop() == "pre-sub1"
+        """
+            )
+        )
+        sub1.join("test_1.py").write(
+            textwrap.dedent(
+                """\
+            from .. import values
+            def test_1(fix):
+                assert values == ["pre-sub1"]
+        """
+            )
+        )
+        sub1_2 = sub1.mkdir("sub1_2")
+        sub1_2.ensure("__init__.py")
+        sub1_2.join("conftest.py").write(
+            textwrap.dedent(
+                """\
+            import pytest
+            from ... import values
+            @pytest.fixture(scope="package")
+            def fix(fix):
+                values.append("pre-sub1_2")
+                yield values
+                assert values.pop() == "pre-sub1_2"
+        """
+            )
+        )
+        sub1_2.join("test_1_2.py").write(
+            textwrap.dedent(
+                """\
+            from ... import values
+            def test_1_2(fix):
+                assert values == ["pre-sub1", "pre-sub1_2"]
+        """
+            )
+        )
+        sub2 = root.mkdir("sub2")
+        sub2.ensure("__init__.py")
+        sub2.join("conftest.py").write(
+            textwrap.dedent(
+                """\
+            import pytest
+            from .. import values
+            @pytest.fixture(scope="package")
+            def fix():
+                values.append("pre-sub2")
+                yield values
+                assert values.pop() == "pre-sub2"
+        """
+            )
+        )
+        sub2.join("test_2.py").write(
+            textwrap.dedent(
+                """\
+            from .. import values
+            def test_2(fix):
+                assert values == ["pre-sub2"]
+        """
+            )
+        )
+        reprec = testdir.inline_run()
+        reprec.assertoutcome(passed=3)
 
 
 def test_call_fixture_function_error():

The difference in the tests is that on test_multiple_packages, there’s no fixture reuse/extend, while on test_multiple_packages_inherit I “extend” the fix fixture.

On test_multiple_packages_inherit, at teardown, I expect sub_1_2 to cleanup first, then sub_1, but the test fails at sub_2 because it still contains the item added in sub_1(which didn’t ran it’s teardown code yet?

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Comments: 15 (11 by maintainers)

Most upvoted comments

Currently we have the issue that multi level package scope is practically undefined and not safely fixable without major reworks

I haven’t tested the exact scenarios, but I believe this should be fixed with #11646. Please test pip install pytest==8.0.0rc1 and let us know if not!