mock.patch 不能直接 mock 嵌套字典深层键,因其仅做浅层替换,不接管内部数据结构行为;需用 Mock 或 MagicMock 配合 configure_mock 或链式属性赋值模拟嵌套访问与方法。

mock.patch 为什么不能直接 mock 嵌套字典的深层键?
因为 pytest-mock(底层用 unittest.mock)对返回值做的是“浅层替换”——你 patch 的是某个函数或属性,它只控制那个入口点的返回,不接管内部数据结构的动态行为。比如 API 返回 {"data": {"user": {"id": 123, "profile": {...}}}},若直接 return_value={"data": {...}},后续代码里改 resp["data"]["user"]["id"] = 999 是允许的,但如果你期望它像真实响应一样抛出 KeyError 或支持 .get("missing", default),纯字典就不够用了。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 用
Mock或MagicMock替代字典,再通过configure_mock设置嵌套属性,例如:mock_resp = Mock() mock_resp.json.return_value = {"data": {"user": {"id": 123}}} # 或更灵活地支持链式访问: mock_resp.data.user.id = 123 mock_resp.configure_mock(**{"data.user.profile.name": "Alice"}) - 若需完全模拟 requests.Response 行为(如带
.status_code、.json()方法),直接实例化Mock(spec=requests.Response),再设return_value属性 - 避免写
return_value={"data": {"user": {"id": 123}}}后又在测试里反复修改这个字典——这会污染其他测试,且掩盖了真实 API 响应不可变的特性
如何让 mock 的 API 响应支持不同 HTTP 状态码和异常路径?
真实调用中,requests.get() 可能返回 200、401、500,也可能抛出 ConnectionError 或 Timeout。只 mock 成功响应,会让测试遗漏错误处理逻辑。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 用
side_effect控制多态行为:传入列表可按调用顺序返回不同响应;传入异常类会触发抛出 - 示例:模拟首次失败、重试成功
mock_get.side_effect = [ requests.exceptions.ConnectionError("timeout"), Mock(status_code=200, **{"json.return_value": {"ok": True}}) ] - 对 4xx/5xx 响应,别只设
status_code,记得配raise_for_status方法,否则被测代码里调用它不会报错:err_resp = Mock(status_code=401) err_resp.raise_for_status.side_effect = requests.exceptions.HTTPError("401") mock_get.return_value = err_resp
pytest-mock 和 requests-mock 混用会冲突吗?
会,而且冲突方式很隐蔽:两者都拦截 requests 底层调用,但机制不同。pytest-mock 是 Python 层 patch,requests-mock 是在 requests.adapters 上注册 mock adapter。如果先启用了 requests-mock 的 session 级别 mock,再用 mock.patch("requests.get"),后者可能被绕过——因为 requests 已经走不到原始 get 函数了。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 一个项目里只选一种:简单场景用
pytest-mock+patch;需要复现完整 HTTP 流程(含 headers、query params 匹配、多次请求断言)就用requests-mock - 若必须混用,确保
requests-mock的Mocker实例在pytest-mock的mocker之前 fixture teardown,否则可能残留 adapter 导致后续测试网络调用异常 - 检查是否误将
@requests_mock.Mocker()装饰器和mocker.patch同时加在同一个测试函数上——这是典型冗余,且后者大概率失效
复杂嵌套结构的 mock 关键不在“像不像”,而在“行为能不能对得上被测代码的调用链”。很多人卡在 mock.patch.object 的 target 写错模块路径,或者把 return_value 设成 dict 却期待它有 Response 方法——这两处错了,后面所有逻辑都白搭。


















