Why isn't my fixture created once per package with the `"package"` scope?

47 Views Asked by At

I'm trying to understand the behavior of the "package" fixture scope.

I have two tests placed in two different packages, but the fixture with "package" scope only seems generated once while I would've expected it to be generated twice.

Why?

MRE :

Directory structure :

> tree
.
├── __init__.py
├── conftest.py
├── pkg1
│   ├── __init__.py
│   └── test_scope.py
└── pkg2
    ├── __init__.py
    └── test_scope.py

2 directories, 6 files

Files :

  • __init__.py files are empty.
  • conftest.py :
#!/usr/bin/env python3
import pytest

@pytest.fixture(scope="package")
def my_fixture():
    print("Package fixture invoked!")
  • test_scope.py files :
#!/usr/bin/env python3

def test_(my_fixture):
    pass

Result :

> pytest -s
============================ test session starts ============================
platform linux -- Python 3.10.12, pytest-7.4.4, pluggy-1.3.0
rootdir: /home/vmonteco/dojo-pytest/notebook/tests/concepts/fixtures/scope/pb
collected 2 items                                                           

pkg1/test_scope.py Package fixture invoked!
.
pkg2/test_scope.py .

============================= 2 passed in 0.01s =============================

Expected result :

> pytest -s
============================ test session starts ============================
platform linux -- Python 3.10.12, pytest-7.4.4, pluggy-1.3.0
rootdir: /home/vmonteco/dojo-pytest/notebook/tests/concepts/fixtures/scope/pb
collected 2 items                                                           

pkg1/test_scope.py Package fixture invoked!
.
pkg2/test_scope.py Package fixture invoked!
.

============================= 2 passed in 0.01s =============================
1

There are 1 best solutions below

0
MrBean Bremen On

You have nested packages here, specifically pb (judging by the pytest log) with the sub-packages pkg1 and pkg2. The question now is which of these packages is used for the package scope.

From what I understand from a glance at the source code, the package is defined by the location of the fixture, e.g. in your case it would be the top-level package where the conftest.py with the fixture resides (pb), and the package will contain both sub-packages pkg1 and pkg2.

To make it work as you expect, you have to move/copy the conftest.py into both packages (at least the part with the package-scoped fixture).

Thinking about this, this is probably the only way to have a consistent behavior in the case of nested packages. The tests where the fixture is applied may reside in different (sub-)packages, and using the test location to define the package could result in undefined behavior, as the package scope could be different between these tests.

Side note: While this is somewhat logical if thinking about it (never having used package scope myself, this was the first time I thought about this), I would appreciate a more clear explanation in the pytest documentation. It probably makes sense to make a documentation PR for this case...

Edit:

The pytest documentation now reads (emphasis mine):

package: the fixture is destroyed during teardown of the last test in the package where the fixture is defined, including sub-packages and sub-directories within it.

This should make it a bit clearer.