Monkeypatch 是 pytest 提供的临时打补丁工具,本质为 fixture,仅在测试函数或类作用域内生效且自动清理;必须作为参数传入,不可在模块顶层使用,环境变量修改用 setenv/delenv,属性打补丁优先用 setattr,注意路径引用和描述符限制。

Monkeypatch 是 pytest 提供的临时打补丁工具,不是 Python 自带的
它本质是 pytest.fixture,只在测试函数或类作用域内生效,退出即还原。别把它和 unittest.mock.patch 混用——后者需要手动管理生命周期,而 monkeypatch 自动清理,写错地方(比如放在模块顶层)会直接报 ValueError: monkeypatch can only be used as a fixture。
常见错误现象:在 conftest.py 里直接调用 monkeypatch.setenv() 报错;或者在测试函数外使用 monkeypatch 变量,提示未定义。
- 必须作为参数传入测试函数,如
def test_something(monkeypatch): - 不能在
@pytest.mark.parametrize外部提前 patch,否则 patch 时机错乱 - 若需跨多个测试复用 patch,应封装成自定义 fixture,而非反复写
monkeypatch.setattr()
修改环境变量要用 monkeypatch.setenv(),不是 os.environ.update()
os.environ.update() 是永久修改,会影响后续其他测试,且无法自动回滚;monkeypatch.setenv() 才是安全的。尤其在 CI 环境中,残留的 os.environ 变更可能让下游测试读到错误配置。
使用场景:测试依赖 DEBUG=True 或 API_URL 的初始化逻辑。
立即学习“Python免费学习笔记(深入)”;
-
monkeypatch.setenv("DEBUG", "1")—— 字符串值,别传布尔或数字 - 要删环境变量,用
monkeypatch.delenv("CACHE_DIR", raising=False),加raising=False避免键不存在时报错 - 注意大小写:Windows 下环境变量不区分大小写,Linux/macOS 区分,测试时最好按目标平台习惯写
给类属性或模块变量打补丁,优先用 monkeypatch.setattr() 而非重赋值
直接写 some_module.CONST = "new" 看似简单,但一旦测试失败或跳过,这个修改就留在内存里了,污染其他测试。而 monkeypatch.setattr() 保证还原。
参数差异很关键:第一个参数可以是模块对象、类、字符串路径;第二个是属性名(字符串);第三个是新值。
- 改模块级变量:
monkeypatch.setattr("requests.get", mock_get)(字符串路径) - 改类属性:
monkeypatch.setattr(MyClass, "timeout", 0.1)(传类对象 + 属性名) - 改实例属性?不行——
monkeypatch不支持实例绑定,得用mock.patch.object(instance, "attr") - 想 patch 一个函数但保留原行为?用
lambda *a, **kw: original_func(*a, **kw) + 1,别漏掉**kw
patch 对象方法时,monkeypatch.setattr() 和 mock.patch() 行为不同
对实例方法 patch,monkeypatch.setattr(obj, "method", new_func) 是直接替换实例上的绑定方法,但不会影响同类其他实例;而 mock.patch.object(cls, "method") 是 patch 类本身,所有实例都受影响。选哪个取决于你测试的是“某个具体对象的行为”还是“整个类的契约”。
容易踩的坑:用 monkeypatch.setattr() 去 patch 类方法(比如 monkeypatch.setattr(str, "upper", lambda s: "HELLO")),这实际无效——因为 str.upper 是描述符,不是普通属性,得用 mock.patch。
- patch 实例方法 → 用
monkeypatch.setattr(instance, "method", ...) - patch 类方法 / 静态方法 / 内置类型方法 → 用
mock.patch更可靠 - patch 后调用被测函数时,确保该函数确实从被 patch 的对象上取方法,而不是缓存了旧引用
最常被忽略的一点:monkeypatch 不会拦截已导入的引用。比如模块 A 里写了 from os import environ,你在测试里 monkeypatch.setenv() 改的是 os.environ,但 A 模块用的却是本地 environ 变量——这时候 patch 失效。得去 patch A 模块里的 environ,而不是 os.environ。










