为何设置package作用域的pytest fixture未按每个包实例化一次?
关于pytest
package 作用域fixture的疑问 我正在理解pytest的package fixture作用域行为。把两个测试放在不同的包中,但设置package作用域的fixture只生成了一次,而我预期它会生成两次,这是为什么?
最小可复现示例(MRE)
目录结构
> tree . ├── __init__.py ├── conftest.py ├── pkg1 │ ├── __init__.py │ └── test_scope.py └── pkg2 ├── __init__.py └── test_scope.py 2 directories, 6 files
文件内容
- 所有
__init__.py文件为空。 conftest.py:
#!/usr/bin/env python3 import pytest @pytest.fixture(scope="package") def my_fixture(): print("Package fixture invoked!")
- 两个包中的
test_scope.py文件:
#!/usr/bin/env python3 def test_(my_fixture): pass
实际运行结果
> 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 =============================
预期结果
> 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 =============================
解答
问题核心在于package作用域的判定规则和你定义fixture的位置不匹配:
package作用域的生效逻辑:pytest的package作用域fixture,是基于它所在的conftest.py对应的包来确定作用范围的。你把my_fixture放在了根目录的conftest.py中,这个文件属于项目最顶层的根包,因此该fixture的作用域是整个根包,而非pkg1或pkg2这两个子包。子包独立触发fixture的条件:要让每个子包的测试都触发一次
package作用域fixture,需要在pkg1和pkg2各自的目录下创建专属的conftest.py,并在其中定义对应的package作用域fixture。
修改后的解决方案
调整目录结构,给每个子包添加conftest.py:
. ├── __init__.py ├── pkg1 │ ├── __init__.py │ ├── conftest.py │ └── test_scope.py └── pkg2 ├── __init__.py ├── conftest.py └── test_scope.py
每个子包的conftest.py内容一致:
#!/usr/bin/env python3 import pytest @pytest.fixture(scope="package") def my_fixture(): print("Package fixture invoked!")
此时运行pytest -s,两个子包的测试会各自触发一次fixture,和你的预期结果一致。
如果不想重复编写fixture代码,可以通过复用逻辑优化:
- 根目录
conftest.py定义基础实现:
import pytest def _my_fixture_impl(): print("Package fixture invoked!")
- 子包
conftest.py继承并定义作用域:
import pytest from ..conftest import _my_fixture_impl @pytest.fixture(scope="package") def my_fixture(): return _my_fixture_impl()
这种方式既避免了代码冗余,又能保证每个子包的package作用域fixture独立触发。
内容的提问来源于stack exchange,提问作者vmonteco
相关产品推荐
相关产品推荐

