pytest内置神器全掌握:monkeypatch、tmp_path与capsys实用技巧清单
【免费下载链接】pytestThe pytest framework makes it easy to write small tests, yet scales to support complex functional testing项目地址: https://gitcode.com/GitHub_Trending/py/pytest
pytest 是 Python 生态中最流行的测试框架之一,而它真正强大的地方,在于一套开箱即用的内置 fixture:monkeypatch让你安全地打补丁和修改环境变量,tmp_path为每个测试提供独立的临时目录,capsys则能精准捕获 print 输出的 stdout/stderr。本文为你整理这 3 个 pytest 内置工具的完整技巧清单——不安装任何插件,写代码时直接在测试函数参数里声明名字,即可自动注入、自动清理。
3大pytest内置fixture一页速览
在动手之前,先用一张表建立整体印象:
| fixture | 注入方式 | 核心能力 | 自动清理 |
|---|---|---|---|
monkeypatch | def test_xxx(monkeypatch): | 替换属性/函数、改环境变量、切目录 | ✅ 测试结束全部还原 |
tmp_path | def test_xxx(tmp_path): | 每个测试独有的临时目录(Path 对象) | ✅ 默认保留最近 3 次运行 |
capsys | def test_xxx(capsys): | 读取测试中 print 到 stdout/stderr 的内容 | ✅ 结束后恢复原始流 |
三者的共同哲学:测试中做的所有"破坏性"操作,都不应该泄漏到下一个测试。这正是 pytest 让新手写测试毫无心理负担的原因。
monkeypatch:如何安全打补丁而不污染环境
monkeypatch是 pytest 最常被用到的内置 fixture,它提供的是一组"改完必还原"的安全操作,源码位于 src/_pytest/monkeypatch.py。官方文档的完整示例在 doc/en/how-to/monkeypatch.rst。
一行代码替换函数(monkeypatch.setattr)
最常见的场景:被测代码依赖网络请求或用户目录,测试时希望它返回固定值。只需把函数替换掉,测试结束后 pytest 自动恢复原样:
def test_getssh(monkeypatch): monkeypatch.setattr(Path, "home", lambda: Path("/abc")) assert getssh() == Path("/abc/.ssh")setattr的签名见 src/_pytest/monkeypatch.py,还支持直接传字符串目标(如"requests.sessions.Session.request"),无需 import 原模块。
设置环境变量、删除字典项、切换目录
| 常用方法 | 作用 | 源码位置 |
|---|---|---|
setenv/delenv | 安全增删环境变量 | monkeypatch.py#L334 |
setitem/delitem | 安全修改/删除字典项 | monkeypatch.py#L128 |
syspath_prepend | 临时修改sys.path | monkeypatch.py#L363 |
chdir | 临时切换工作目录 | monkeypatch.py#L405 |
小技巧:删除可能不存在的东西时加raising=False,例如monkeypatch.delenv("USER", raising=False),避免"变量本来就不存在"时误报 KeyError。
monkeypatch 避坑三原则 ⚠️
- 别打补丁到内置函数(如
open、compile),可能破坏 pytest 自身; - 优先打"使用方"而非"源头":若你的模块写了
from os import getcwd,应 patchmymodule.getcwd而不是os.getcwd; - 用 context 限定作用域:
with monkeypatch.context() as m:可以把补丁限制在代码块内,适合复杂 fixture(见 monkeypatch.py#L149)。
tmp_path:给每个测试一个独立临时目录
测试中要写文件?千万不要在源码目录里制造垃圾文件。tmp_path会自动提供一个pathlib.Path类型的临时目录,且每个测试函数都是全新的、互不干扰的。定义见 src/_pytest/tmpdir.py#L286。
def test_create_file(tmp_path): p = tmp_path / "hello.txt" p.write_text("content", encoding="utf-8") assert p.read_text(encoding="utf-8") == "content"tmp_path 与 tmp_path_factory 怎么选
- 单个测试用→ 直接用
tmp_path,最简单; - 多个测试共享一份大文件(如生成的测试图片)→ 用会话级的 tmp_path_factory,配合
@pytest.fixture(scope="session")只生成一次,避免重复计算。
临时目录在哪里?会留多久?
默认结构为{系统临时目录}/pytest-of-{用户}/pytest-{序号}/{测试名}/,pytest 会自动保留最近 3 次运行的目录,方便测试失败时回查现场。想自定义位置可用--basetemp参数,想调整保留策略可配置tmp_path_retention_count。详细规则见 doc/en/how-to/tmp_path.rst。
💡 排错神器:故意让最后一条断言失败,pytest 输出中会显示
tmp_path = PosixPath('.../test_xxx0'),直接找到磁盘上的真实目录检查残留文件。
capsys:在测试中拦截 print 输出
想断言"某函数会打印特定内容"?用capsys就能在测试内部直接读取被捕获的输出,源码在 src/_pytest/capture.py#L1013。
def test_myoutput(capsys): print("hello") captured = capsys.readouterr() assert captured.out == "hello\n"readouterr 的快照机制
readouterr()返回一个带out和err属性的命名元组,并且是快照式的:调用后缓冲区清空,之后打印的内容要再调一次才能读到——这让"先打印、再断言、再打印、再断言"的分步验证成为可能。
4个兄弟fixture,按需选择
| fixture | 捕获层级 | 返回类型 | 适用场景 |
|---|---|---|---|
capsys | Python 的sys.stdout | str | 一般 print 断言(最常用) |
capsysbinary | 同左 | bytes | 被测代码写二进制数据 |
capfd | 操作系统文件描述符 1/2 | str | 需要捕获 C 扩展、子进程输出 |
capfdbinary | 同左 | bytes | fd 级 + 二进制 |
另外,测试中可以用with capsys.disabled():临时关闭捕获,让输出直接打到终端调试;命令行加-s则是全局关闭捕获。完整说明见 doc/en/how-to/capture-stdout-stderr.rst。
实用技巧清单:5条经验一次带走 ✅
- 参数声明即注入:三个 fixture 都不需要 import,测试函数签名里写上名字就能用;
- 共享逻辑抽 fixture:
monkeypatch常与自定义@pytest.fixture组合,把"打补丁"封装成mock_response这样的语义化 fixture; - 失败现场可回查:
tmp_path保留最近 3 次运行结果,断言失败时去磁盘看真实文件内容; - 输出断言优先 capsys:比解析 JUnit 报告或用 print 肉眼检查精确得多;
- 临时文件用完就完:
monkeypatch的改动、capsys的流恢复都由 pytest 自动处理,测试函数里不用写一行还原代码。
延伸阅读:源码与官方文档入口
| 资料 | 路径 |
|---|---|
| monkeypatch 使用指南 | doc/en/how-to/monkeypatch.rst |
| tmp_path 使用指南 | doc/en/how-to/tmp_path.rst |
| stdout/stderr 捕获指南 | doc/en/how-to/capture-stdout-stderr.rst |
| 全部 fixture API 参考 | doc/en/reference/fixtures.rst |
| monkeypatch 源码 | src/_pytest/monkeypatch.py |
| tmp_path 源码 | src/_pytest/tmpdir.py |
| capsys 捕获源码 | src/_pytest/capture.py |
掌握monkeypatch、tmp_path与capsys这三个内置 fixture,你就拥有了 pytest 日常测试中最核心的"三件套":改环境、造数据、验输出,且全部自动善后——这就是 pytest "写小测试很简单,扩展也很强" 的最佳注脚。
【免费下载链接】pytestThe pytest framework makes it easy to write small tests, yet scales to support complex functional testing项目地址: https://gitcode.com/GitHub_Trending/py/pytest
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考