session作用域fixture复用有状态对象导致401、keyerror等错误,根源是状态污染与跨进程/跨测试隔离缺失,而非语法错误。

session作用域下fixture被复用,但内部状态已失效
报错往往不是因为scope="session"语法错误,而是它强制复用了有状态的对象——比如带headers的requests.Session()实例、缓存了过期auth_token的工具类,或修改了os.environ的初始化逻辑。pytest不会阻止你写,但运行时会暴露副作用。
典型现象包括:
-
401 Unauthorized:多个测试共享同一个登录token,后一个测试发起请求时token已过期或被前一个测试登出 -
KeyError或AttributeError:fixture返回对象在第一次测试中被就地修改(如obj.config["timeout"] = 30),后续测试读到脏值 - 数据库断言失败:session级DB连接未做事务隔离,A测试插入数据后B测试查到了不该看到的记录
多进程并行(xdist)时session fixture实际不共享
用pytest -n auto跑测试时,每个worker进程都会独立执行一次scope="session"的fixture——它并不是跨进程单例。你以为的“全局只跑一次”,其实是“每个进程各跑一次”,还可能因初始化顺序不同导致竞态。
容易踩的坑:
- 在session fixture里启动本地mock server(如
httpx.MockTransport),结果每个worker都绑同一个端口,第二个直接报Address already in use - 依赖
tempfile.mkdtemp()创建临时目录,但没加进程前缀,多个worker写进同一路径造成覆盖或权限冲突 - 用
threading.local()试图隔离状态,却忘了multiprocessing下线程local完全无效
fixture之间依赖越界触发隐式报错
pytest要求子fixture的作用域不能比父fixture更长。比如你定义了一个scope="function"的db_session,但它依赖另一个scope="session"的engine——这合法;但如果反过来,scope="session"的fixture去依赖scope="function"的test_data,pytest会在收集阶段就抛ScopeMismatch异常。
常见错误模式:
-
@pytest.fixture(scope="session") def api_client(request_util): ...,而request_util是function级——直接报错 - 参数化fixture(
@pytest.mark.parametrize)套在session fixture上,误以为能复用,实际每次参数都新建实例,违背session本意 - 用
autouse=True的module级fixture偷偷改了全局logger配置,结果session级fixture读到被污染的日志handler
没做teardown导致资源残留和内存泄漏
session作用域意味着fixture实例存活整个测试会话,如果你用return直接返回一个大对象(比如加载了10万条测试数据的pandas.DataFrame),它就不会被GC回收,直到pytest进程退出。更危险的是,如果它持有文件句柄、数据库连接或网络socket,这些资源也不会自动释放。
正确做法是用yield代替return:
import tempfile <p>@pytest.fixture(scope="session") def temp_dir(): path = tempfile.mkdtemp() yield path</p><h1>这里显式清理,否则path一直占内存+磁盘</h1><pre class="brush:python;toolbar:false;">import shutil shutil.rmtree(path)
注意:yield后的清理代码只在session结束时执行一次,不是每个测试后都跑。
真正难调试的,是那些不报错但行为漂移的问题——比如token看起来有效,实则签名时间戳错位;或者数据库连接没断,但事务上下文混了。这类问题不会抛异常,只会让断言随机失败。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











