tmp_path不能自动清理全局变量,因为它仅隔离文件系统路径,不干预python命名空间;全局变量(如配置、日志、缓存)若在模块顶层初始化或硬编码绝对路径,会跨测试复用旧值,导致状态污染。

为什么 pytest 的 tmp_path 不能自动清理全局变量?
因为 tmp_path 只管文件系统隔离,不碰 Python 的命名空间。如果你在测试前把某个全局变量(比如 CONFIG_PATH 或 logger.handlers)指向了真实路径或单例对象,后续测试即使用了不同的 tmp_path,那个变量还指着旧值——污染就发生了。
常见现象:test_a 写了 config.load('/tmp/old'),test_b 用 tmp_path / "config.yaml",但 config 模块里缓存的路径/内容没重置,直接读错文件或复用旧状态。
- 全局变量污染和
tmp_path是正交问题:一个在内存,一个在磁盘 -
tmp_path每次测试确实新建目录、自动清理,但不会 reload 模块、不会重置模块级变量 - 最容易被忽略的是 logging、config、cache 类模块,它们常在模块顶层初始化单例
如何在每个测试前重置关键全局变量?
用 pytest.fixture(autouse=True) 配合 importlib.reload() 或显式重置。别依赖“测试顺序”或“手动清空”,pytest 不保证执行顺序,且 autouse fixture 会自动注入所有测试。
示例:重置一个加载配置的模块
import importlib import pytest <p>@pytest.fixture(autouse=True) def reset_config_module(): import config # 假设这是你的配置模块 importlib.reload(config) # 强制重新执行模块顶层代码 yield</p><h1>如果需要,这里可加 teardown 清理(如关闭临时 handler)</h1><p></p>
- 只对真正有副作用的模块 reload;频繁 reload 会影响性能,但测试中可接受
- 如果模块里用了
os.chdir()或修改了sys.path,也要在 fixture 中还原 - 避免 reload 标准库或深度依赖的第三方模块(如
requests),可能引发不可预知行为
用 tmp_path 时怎样避免“路径写死”导致的隐性污染?
污染常来自测试代码里硬编码路径,比如 open("/tmp/test.db", "w") —— 这完全绕过了 tmp_path,多个测试共用同一路径,互相覆盖。
正确做法:所有 I/O 必须基于 tmp_path 构造,且优先用 tmp_path 而非 tmp_path_factory(后者适合跨测试共享,反而容易引入耦合)。
- 错误写法:
Path("/tmp/data.json").write_text(...) - 正确写法:
(tmp_path / "data.json").write_text(...) - 如果函数内部必须接收路径,就传
str(tmp_path / "xxx"),而不是拼字符串 - 检查日志、缓存、数据库连接等是否也悄悄用了绝对路径——它们比文件更难发现
logging 和 tempfile 模块特别容易踩坑
这两个模块的默认行为会偷偷逃出 tmp_path 隔离:logging 默认往 stderr 或固定文件写;tempfile.mktemp() 生成的路径不归属 tmp_path,且已被弃用。
解决方式:
- logging:在 autouse fixture 中调用
logging.getLogger().handlers.clear(),并确保所有 handler 的baseFilename显式设为tmp_path / "app.log" - tempfile:彻底禁用
mktemp();改用tempfile.NamedTemporaryFile(dir=tmp_path)或直接用tmp_path / "xxx" - 注意:某些库(如
pandas.read_csv())内部可能调用tempfile,需查文档确认是否支持传入tempdir参数
真正的隔离不是靠一个 tmp_path 参数,而是整条数据流——从路径生成、到模块初始化、再到运行时 IO——都得串在同一个临时上下文里。漏掉任意一环,污染就藏在你以为已经干净的地方。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











