最推荐用monkeypatch.setenv()修改环境变量,作用域限于单个测试函数且自动还原;禁用os.environ直接赋值;conftest.py中全局patch会破坏隔离性;优先选monkeypatch而非mock.patch.dict。

pytest中用monkeypatch.setenv修改环境变量最直接
直接用 monkeypatch.setenv() 就能覆盖当前测试进程的 os.environ,且作用域严格限制在单个测试函数内,测试结束自动还原——这是最安全、最推荐的方式。它不污染全局状态,也不需要手动清理。
常见错误是试图用 os.environ['KEY'] = 'value' 手动赋值,这会导致后续测试被意外影响,而且 pytest 不会帮你回滚。
-
monkeypatch.setenv('API_URL', 'http://localhost:8000'):设置字符串值,自动转为str -
monkeypatch.setenv('DEBUG', '1', prepend=False):第三个参数仅对PATH类路径变量有意义,普通变量忽略 - 若需清空某个变量,用
monkeypatch.delenv('CONFIG_FILE', raising=False),raising=False避免键不存在时报错
为什么不能在conftest.py里全局patch os.environ?
因为 os.environ 是模块级单例,一旦在 conftest.py 的 pytest_configure 或 fixture 中直接修改,所有测试都会共享这个状态,违背 pytest 的隔离原则。尤其当多个测试并行(-n auto)时,行为不可预测。
正确做法是把 monkeypatch 作为函数级 fixture 注入,例如:
def test_with_custom_env(monkeypatch):
monkeypatch.setenv('ENV', 'test')
assert get_env_config() == 'test'
这样每个测试拿到的是干净、独立的环境副本。
mock.patch.dict('os.environ', {...}) 和 monkeypatch.setenv 选哪个?
优先选 monkeypatch.setenv。虽然 mock.patch.dict('os.environ', {...}) 也能用,但它在 patch 结束时会**完全重置** os.environ 为原始快照,可能意外删掉其他测试或库依赖的环境变量(比如 PYTHONPATH、HOME),引发隐性故障。
-
monkeypatch.setenv只增/改/删指定键,其余不变 -
mock.patch.dict是“全量快照+全量恢复”,适合需要严格控制整个环境的极端场景 - 两者都不影响子进程的环境——子进程默认继承父进程
os.environ,但如果你在测试里调用了subprocess.run(..., env=...),记得显式传入修改后的os.environ.copy()
遇到 os.getenv() 返回 None?检查是否漏了 monkeypatch 参数
典型现象:测试里调用 os.getenv('DB_HOST') 得到 None,但明明写了 monkeypatch.setenv('DB_HOST', 'localhost')——大概率是测试函数签名里没写 monkeypatch 参数,pytest 根本没注入这个 fixture。
确认点:
- 函数定义必须含
monkeypatch参数(名字不能错) - 不要写成
mp或patcher等别名,pytest 只认monkeypatch这个 fixture 名 - 如果用了自定义 fixture 依赖
monkeypatch,需显式声明,例如@pytest.fixture def my_env_fixture(monkeypatch):
环境变量的读取时机也很关键:有些库(如 logging.config 或 pydantic.BaseSettings)在模块导入时就缓存了环境值,monkeypatch 必须在 import 前生效——这时得把 patch 放进测试函数开头,而不是依赖 fixture 自动注入顺序。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











