pytest中fixture作用域设置不当会导致测试间状态污染,最常见原因是共享可变对象(如列表、字典、单例实例)且未隔离;默认function作用域若返回同一引用仍会互相干扰,需确保每次返回新对象或显式清理。

pytest 中 fixture 的作用域设置不当会导致状态污染
测试间状态污染最常见原因是共享了可变对象(比如列表、字典、类实例)且未隔离。pytest 默认的 function 作用域看似安全,但若 fixture 返回的是同一个可变对象引用,多个测试仍会互相干扰。
例如:一个返回 dict() 的 fixture 被两个测试调用,如果其中一个测试修改了该字典内容,另一个测试拿到的就是已被污染的字典——因为 dict() 每次都新建,这没问题;但如果 fixture 缓存了单例或复用了全局对象,问题就来了。
- 检查 fixture 是否返回了全局变量、模块级对象或单例实例
- 避免在 fixture 中直接返回
my_list(已定义的可变对象),应返回my_list.copy()或list(my_list) - 对需要隔离的资源(如临时文件、数据库连接),优先使用
function作用域;跨测试共享只在明确需要时才用class或module
用 autouse=True + function 作用域重置共享状态
当某些状态(如全局配置、缓存字典、第三方库内部状态)无法通过 fixture 参数注入控制时,可以用自动运行的 fixture 强制清理。
典型场景:某 SDK 内部维护了一个静态 _cache 字典,多个测试调用其方法后缓存被填充,后续测试结果异常。
- 定义一个带
autouse=True和scope="function"的 fixture,在每次测试前后清空缓存 - 确保清理逻辑放在
yield之前(setup)和之后(teardown),或用addfinalizer - 不要依赖
__del__或弱引用做清理,pytest 不保证执行时机
@pytest.fixture(autouse=True, scope="function")
def clear_sdk_cache():
from my_sdk import _cache
_cache.clear()
yield
_cache.clear() # 防止测试中途异常退出导致残留
数据库/外部服务测试必须用 transaction rollback 或 fresh instance
单元测试中如果涉及真实数据库写入,仅靠 fixture 作用域无法防止污染——事务未回滚、表数据残留、自增 ID 偏移都会影响后续测试。
常见错误是只创建一次连接、复用同一个 session,或在 module 级 fixture 中初始化 DB 并不做 cleanup。
- 用
function级 fixture 创建新连接 + 启动事务,测试结束调用rollback() - 避免在 fixture 中执行
session.commit();所有写操作应在测试内显式触发,且由 fixture 自动回滚 - 对 SQLite 可用
:memory:数据库;对 PostgreSQL/MySQL,建议每个测试用随机 schema 名或临时数据库 - 注意 ORM 的 identity map 缓存(如 SQLAlchemy 的
session.identity_map),需在每次测试前清空或新建 session
mock.patch 的作用域和 stop 时机容易引发泄漏
用 mock.patch 替换模块级函数或类时,若 patch 没正确停止,后续测试可能继续走 mock 分支,造成行为错乱。
现象包括:测试 A 打了 patch,测试 B 意外命中 mock 返回值;或 patch 对象本身被修改后影响其他测试。
- 优先使用 fixture 封装
patch,而非在测试函数内用with语句——后者在异常时可能跳过stop() - 用
pytest-mock提供的mockerfixture,它自动管理生命周期,比原生patch更可靠 - 避免 patch 类属性(如
MyClass.config),改用 patch 实例方法或依赖注入方式替换行为 - 若必须 patch 模块级对象,确认 patch 的 target 是完整路径(如
"requests.get"),而不是相对导入后的别名
状态污染不是玄学,而是可追踪的引用传递和生命周期失控。最常被忽略的是第三方库的内部状态、mock 的隐式持久化、以及“看起来只读实则可变”的返回值——盯住这些点,比堆砌隔离策略更有效。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











