session级fixture对象未被回收是因为其生命周期贯穿整个测试会话,pytest不主动触发垃圾回收,尤其当对象持有文件句柄、数据库连接等资源时会导致内存持续累积。

fixture scope设为session时,为什么测试后对象没被回收?
因为 session 级 fixture 创建的对象生命周期贯穿整个测试会话,pytest 不会主动调用 __del__ 或触发垃圾回收——尤其当对象持有文件句柄、数据库连接、线程或全局缓存时,内存占用会持续累积。
常见现象:运行 pytest --tb=short -v 后,psutil.Process().memory_info().rss 显示内存持续上涨;或测试结束后用 gc.get_objects() 发现大量残留实例。
- 确认是否真有泄漏:在 fixture teardown 阶段手动触发
gc.collect()并检查len(gc.get_objects(YourClass)) - 避免在
sessionfixture 中直接返回带状态的类实例(如DatabaseClient()),改用工厂函数 + 显式清理 - 若必须用
session级资源,务必配对使用yield和 teardown 逻辑,且 teardown 中显式关闭/清空/置为None
如何安全地在session fixture中管理数据库连接?
直接返回一个 SQLAlchemy Engine 或 psycopg2 connection 是危险的:连接池内部可能缓存大量语句、游标未关闭、连接未归还池,导致 fd 耗尽或内存不释放。
正确做法是把连接创建和销毁控制权交还给单个测试,session fixture 只负责提供配置或初始化一次的共享资源(如连接字符串、池参数)。
立即学习“Python免费学习笔记(深入)”;
- 不要写:
@pytest.fixture(scope="session") def db_engine(): return create_engine(...) - 应该写:
@pytest.fixture(scope="session") def db_url(): return os.getenv("TEST_DB_URL"),再让每个 test function 自己创建/关闭 engine - 若需复用连接池(如减少建连开销),用
scope="session"返回create_engine(..., pool_pre_ping=True, pool_recycle=3600),但必须在 teardown 中调用engine.dispose()
teardown阶段忘记调用close()导致的泄漏怎么定位?
典型错误是写了 yield resource 却漏掉后续的 resource.close() 或 resource.shutdown(),或者 close 抛异常后未被 pytest 捕获,导致 teardown 中断执行。
pytest 默认会吞掉 teardown 中的异常,所以你根本看不到报错,但资源就卡在那里了。
- 始终用
try...finally包裹 teardown 逻辑:yield res; finally: res.close() - 对关键资源(如
threading.Thread、asyncio.EventLoop、tempfile.TemporaryDirectory)启用weakref.finalize做兜底清理 - 加日志:
logging.info("tearing down %s", id(resource)),对比 setup 和 teardown 日志行数是否匹配
使用autouse=True的module fixture引发的隐式引用怎么办?
autouse=True 的 module 或 session fixture 会被所有测试自动注入,容易无意中持有了测试函数局部变量的引用(比如通过闭包、装饰器、monkeypatch 注入的 mock 对象),从而阻止 GC 回收。
最隐蔽的是 monkeypatch.setattr 修改了模块级变量,而该变量又引用了测试中创建的大对象。
- 避免对模块级可变对象(如
requests.adapters.DEFAULT_RETRIES)做 patch,改用with块内 patch - 检查
sys.getrefcount(obj)在 fixture setup/teardown 前后是否下降;若不变,说明仍有活跃引用 - 禁用 autouse,显式声明依赖,能更清晰看到谁在 hold 这个 fixture 实例
close() 但没处理它可能抛出的 AttributeError 或 RuntimeError,导致后续清理代码跳过。别假设 close 总是安静执行。


















