pytest中fixture复用需严守生命周期与作用域:默认用function级,避免全局状态污染;依赖名须严格匹配;测试数据优先用factory_boy解耦模型约束;跨模块复用仅通过conftest.py自动查找,禁用直接import;mock统一用pytest-mock的mocker fixture确保自动清理。

pytest 中 fixture 复用:别直接写全局变量
测试代码复用最常踩的坑,是把共享逻辑塞进 conftest.py 里就以为万事大吉。fixture 不是函数集合,它有明确的生命周期和作用域约束。
常见错误现象:AttributeError: 'NoneType' object has no attribute 'id',往往是因为 fixture 返回了 None 却被当成对象用了;或者多个测试并发跑时状态污染,比如数据库连接被提前 close。
- 作用域选
scope="function"是默认且最安全的起点;只有确认无副作用、纯计算类逻辑(如生成固定 token),才考虑scope="session" - fixture 函数体内避免直接操作外部状态(如修改全局字典、写文件);要用就封装在
yield块里,确保 teardown 可靠 - 依赖其他 fixture 时,参数名必须和被依赖 fixture 的函数名完全一致,大小写敏感,拼错就静默 fallback 成
None
测试数据生成:用 factory_boy 而不是手写 dict
手动拼 {'name': 'test', 'email': 't@e.st', 'created_at': datetime.now()} 看似快,但一改字段类型或新增校验,所有测试都得同步修,复用性反而最低。
factory_boy 的核心价值不是“少写几行”,而是把模型约束和测试数据解耦。你改了 Django Model 的 email 字段为 UniqueConstraint,只需更新 UserFactory,不用翻遍所有 test_*.py。
立即学习“Python免费学习笔记(深入)”;
使用模板与程序分离的方式构建,依靠专门设计的数据库操作类实现数据库存取,具有专有错误处理模块,通过 Email 实时报告数据库错误,除具有满足购物需要的全部功能外,成新商城购物系统还对购物系统体系做了丰富的扩展,全新设计的搜索功能,自定义成新商城购物系统代码功能代码已经全面优化,杜绝SQL注入漏洞前台测试用户名:admin密码:admin888后台管理员名:admin密码:admin888
-
UserFactory.build()生成未保存实例,适合单元测试;UserFactory.create()才真正落库,适合集成测试 - 避免在 factory 中调用耗时操作(如发邮件、调外部 API),这类行为应由 fixture 显式控制启停
- 不同测试场景用
params或class Params定义变体,而不是复制粘贴整个 factory 类
跨模块测试复用:通过 conftest.py 暴露 fixture,而非 import
看到同事在 test_api.py 里 from tests.unit.conftest import db_session,基本可以判定后续会出问题——pytest 的 fixture 查找机制不走 Python import 路径,硬导会绕过作用域管理,导致 fixture 不执行 setup/teardown。
正确路径只有一条:把 fixture 函数放在 conftest.py 中,并确保该文件位于测试模块的父目录下(pytest 自动向上查找)。子目录可再放自己的 conftest.py 覆盖或扩展。
- 同名 fixture 在更近的
conftest.py中会覆盖外层定义,这是可控的覆盖,不是冲突 - 如果需要“有条件启用”某个 fixture(比如只在 CI 环境加载 mock),用
if os.getenv('CI'):判断,别试图用 pytest 配置项动态注册 - 不要在
conftest.py里写业务逻辑或 import 应用代码的 models —— 它只负责测试上下文,不是启动器
mock 复用:用 pytest-mock 插件,别自己 patch
手写 @patch('xxx.yyy.func') 看起来直观,但一旦测试类继承、fixture 嵌套、作用域混用,patch 就容易漏掉或重复,mock 对象状态混乱,报错信息还指向 patch 行而不是真实调用点。
pytest-mock 提供的 mocker fixture 是绑定到每个测试函数的,自动 cleanup,且支持链式调用,比如 mocker.patch.object(cls, 'method').return_value = 42,比原生 patch 更贴近测试意图。
- mock 对象尽量只 stub 必需返回值,别过度配置 side_effect;复杂逻辑应抽成真实 helper 函数再 mock 其调用
- 对异步函数 mock,必须用
mocker.patch('mod.async_func', new_callable=AsyncMock),普通Mock无法 await - 不要 mock 内置函数(如
open,print)除非必要;优先考虑重写测试路径(比如用临时目录 +tmp_pathfixture 替代 mock 文件系统)
复用的本质不是“写一次用多次”,而是“改一处,全量生效”。越想省事跳过作用域、生命周期、边界条件的判断,后面 debug 花的时间越不成比例。fixture 名、factory 类名、conftest 位置,这些看着琐碎的约定,其实是隔离变化的最小单位。









