mock.patch 必须在测试函数执行前生效,因为它仅拦截后续的导入或属性访问;若模块已提前导入,则无法替换原始对象。

mock.patch 为什么必须在测试函数执行前生效
因为 patch 实际上是在目标模块被导入、代码被执行前,临时替换成 Mock 对象。如果模块已在测试前被导入(比如写在文件顶部),patch 就无法拦截原始导入行为——它只作用于后续的 import 或属性访问。
常见错误现象:ModuleNotFoundError 没出现,但 mock 没生效,被测函数仍调用真实模块。
- 正确做法:在
test_*函数内部使用with patch("module.submodule.function") as mock_func: - 或用装饰器:在测试函数上加
@patch("requests.get"),注意装饰器顺序(越靠近函数的越先执行) - 绝对路径必须从被测代码「看到的导入路径」出发,不是你本地文件路径。例如被测代码写了
from requests import get,要 patch"mymodule.get";如果写了import requests,则 patch"mymodule.requests.get"
mock.return_value 和 mock.side_effect 的关键区别
return_value 是静态返回,适合模拟固定响应;side_effect 支持抛异常、返回不同值、甚至调用真实函数,更贴近真实交互逻辑。
使用场景:需要测试 HTTP 请求失败、数据库查询空结果、多次调用返回不同数据时,side_effect 不可替代。
-
mock_func.return_value = {"status": "ok"}→ 每次调用都返回这个 dict -
mock_func.side_effect = [ValueError("timeout"), {"data": 42}]→ 第一次抛异常,第二次返回 dict -
mock_func.side_effect = lambda url: {"url": url, "cached": True}→ 支持参数感知的动态返回
如何安全地 mock 全局变量或类实例方法
全局变量(如配置字典、连接池单例)和实例方法容易因 patch 位置不当导致状态污染,影响其他测试用例。
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
性能与兼容性影响:对类方法 patch 过深(如 patch("module.Class.method"))可能绕过 __init__ 或属性初始化逻辑,导致断言失败但原因难查。
- 优先 patch 被测模块中「使用该变量/方法的位置」,而不是定义位置。例如模块 A 定义了
DB_POOL,模块 B 导入并使用了它,测试 B 时应 patch"B.DB_POOL" - mock 类实例方法时,确保 patch 的是绑定后的方法(即实例调用路径),而非类定义本身,否则
self可能为 None - 用
autospec=True参数可自动继承原函数签名,防止调用时参数错位却不报错
AssertionError 显示 “Expected call not found” 怎么快速定位
这不是 mock 失效,而是调用记录和预期不匹配——可能参数类型不对、调用次数少一次、或者用了 assert_called_once() 但实际调用了两次(包括隐式调用)。
容易踩的坑:字符串和 bytes 混用、字典 key 顺序(Python 3.7+ 有序但测试中别依赖)、浮点数直接比较。
- 用
mock_func.assert_called_with("https://api.com", timeout=5)前,先打印mock_func.call_args看实际传了什么 - 检查是否误用了
assert_called_once():它等价于assert_called() and call_count == 1,而有些库会内部调用多次(如 logging 配置) - 避免在 patch 中用
new_callable=MagicMock后又手动设return_value,这会覆盖 autospec 行为,让参数校验失效
mock 最难的不是写法,是判断“该在哪儿 patch”和“该 mock 到哪一层”。多看被测函数里 import 和调用的实际路径,比背文档更管用。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










