pytest 如何写 fixture 自动为每个测试用例创建临时目录

pytest内置tmp_path fixture最简单可靠:每个测试前自动创建独立临时目录,测试后自动清理,返回pathlib.Path对象,无需手动管理。

pytest 中用 tmp_path fixture 最简单可靠

不用自己写 fixture,pytest 内置的 tmp_path 就是专为这个场景设计的:每个测试函数运行前自动创建独立临时目录,测试结束自动清理。它返回的是 pathlib.Path 对象,开箱即用。

  • 直接在测试函数参数中声明 tmp_path,pytest 会自动注入
  • 该目录路径唯一、隔离,不同测试间互不影响
  • 无需手动 os.makedirsshutil.rmtree,失败时也会尝试清理
  • 比旧版 tmpdir(返回 py.path.local)更现代、类型更清晰
def test_something(tmp_path):
    data_file = tmp_path / "input.txt"
    data_file.write_text("hello")
    assert data_file.read_text() == "hello"

需要自定义临时目录结构?用 @pytest.fixture 包装 tmp_path

如果每个测试都需要预置子目录、配置文件或特定权限,可以在 fixture 中基于 tmp_path 构建,而不是从头 mktemp。这样既保留自动生命周期管理,又复用 pytest 的清理逻辑。

  • fixture 作用域设为 "function"(默认),确保每测一个新目录
  • 不要在 fixture 里调用 tempfile.mkdtemp() —— 那会脱离 pytest 管理,导致残留
  • 可以安全地 mkdirtouchchmod,所有操作都在 tmp_path
@pytest.fixture
def project_layout(tmp_path):
    (tmp_path / "src").mkdir()
    (tmp_path / "tests").mkdir()
    (tmp_path / "pyproject.toml").write_text("[build-system]")
    return tmp_path

def test_build_runs(project_layout):
    assert (project_layout / "src").exists()

为什么不用 tempfile.TemporaryDirectory()

手动用 tempfile.TemporaryDirectory() 容易出错:忘记 __enter__/__exit__、异常时未清理

、多线程下路径冲突。pytest 的 tmp_path 在底层其实也用了类似机制,但封装了所有边界情况。

  • TemporaryDirectory 返回字符串路径,不是 Path,操作更啰嗦
  • 若测试崩溃或被信号中断,它的 __exit__ 可能不执行,留下脏目录
  • pytest 的清理逻辑会捕获测试异常、中断甚至 Ctrl-C,并尽力删除
  • tmp_path_factory 配合还能跨测试共享(如 session 级缓存),TemporaryDirectory 做不到

注意 tmp_pathtmp_path_factory 的分工

tmp_path 是函数级临时目录,够大多数场景;只有当你需要在 fixture 中动态生成多个目录、或想控制根路径位置(比如统一放在 SSD 分区),才用 tmp_path_factory

  • tmp_path_factory.mktemp("name") 创建子目录,仍受 pytest 清理管辖
  • tmp_path_factory.getbasetemp() 能查到所有临时目录父路径,调试时有用
  • 别把 tmp_path_factory 当成替代品 —— 多数时候,直接参数化 tmp_path 更直白

真正容易被忽略的是:临时目录名带随机后缀,但路径本身不加密、不隐藏;如果测试里打印了 tmp_path,CI 日志里会暴露真实路径结构 —— 这在审计敏感环境时可能是个小隐患。