fixture 的 scope 参数控制其生命周期和复用范围,决定同一实例被多少测试共用;scope 越大复用越广但状态污染风险越高,function/class/module/session 对应函数/类/模块/会话级复用,错误设置易致断言失败、状态泄漏或资源残留。

fixture 的 scope 参数控制什么
它决定 fixture 实例的生命周期和复用范围——不是“执行几次”,而是“同一个 fixture 实例被多少个测试共用”。scope 越大,复用越广,但副作用风险越高(比如状态污染、资源未清理)。
常见误判是认为 scope="session" 就一定比 scope="function" “快”,其实如果 fixture 初始化开销大但内部有状态依赖,反而可能因共享导致测试失败,被迫加锁或重置,实际更慢。
function / class / module / session 四个取值的实际行为差异
它们对应不同层级的复用边界,关键看 pytest 如何组织测试执行单元:
-
scope="function":每个测试函数独享一个 fixture 实例;最安全,也是默认值 -
scope="class":同一测试类(class TestFoo:)内所有方法共享一个实例;注意类内测试顺序不可控,不能依赖执行先后 -
scope="module":同一 Python 模块(.py 文件)内所有测试函数和类共享一个实例;适合模块级初始化(如临时目录、DB 连接池) -
scope="session":整个 pytest 会话(一次pytest命令)中只创建一次;必须确保线程/进程安全,且不能有 tearDown 类逻辑
注意:scope="package" 存在但极少用,仅当测试分散在多个模块又同属一个包、且需跨模块共享时才考虑,容易引发隐式耦合。
scope 设置错误的典型报错和现象
pytest 不会直接报 “scope 错了”,而是暴露为测试行为异常:
- 用
scope="session"返回了一个数据库连接对象,但多个测试并发写入后断言失败 → 实际是连接被复用且未事务隔离 - 用
scope="module"初始化了全局计数器变量,结果第二个测试读到非零值 → 状态泄漏 - 在
scope="class"fixture 中调用了yield,但 teardown 部分在类最后一个测试结束后才执行,中间测试已开始 → 日志或资源残留 - 使用
--workers或xdist并行时,scope="session"fixture 在各 worker 进程中各自执行一次,不是真正“单例” → 误以为能共享状态
如何安全地升级 scope(从 function 到更大范围)
不能只改参数,必须同步检查 fixture 内部是否满足该 scope 的约束:
- 移除所有依赖“首次调用”或“调用次数”的逻辑(如
if not hasattr(...)缓存判断) - 确认返回对象是线程安全的(如用
threading.local()包装,或改用无状态对象) - 若含
yield,teardown 代码必须能多次执行无副作用(比如删除临时文件前先检查是否存在) - 在 CI 中强制开启
--workers=2测试并行行为,避免本地单进程跑过就上线
最常被忽略的一点:scope 改变后,fixture 名称本身不会触发 pytest 重新识别依赖关系——如果测试函数签名没变,pytest 可能复用旧缓存,建议加 --cache-clear 或改个 fixture 名做验证。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











