根本原因是patch路径必须匹配函数被导入和使用的位置,而非定义位置;如service.py中import requests后调用requests.get,则应patch'service.requests.get',而非'requests.get'。

mock.patch 为什么总 patch 不到目标函数
根本原因不是写法错,而是导入路径写错了——patch 必须按「测试代码里实际导入的位置」去打补丁,而不是按函数定义的位置。比如你在 service.py 里写了 requests.get,但在测试文件中是 from service import fetch_data,那就要 patch 'service.requests.get',而不是 'requests.get'。
常见错误现象:AttributeError: 或 mock 生效但接口仍被真实调用。
- 检查被测函数内部 import 方式(是
import requests还是from requests import get) - 确认 patch 路径以「测试模块能访问到该对象的位置」为准,通常为
模块名.类名.方法名或模块名.函数名 - 用
pytest-mock的mocker.patch比原生patch更安全,它自动 handle scope 和 cleanup
用 mocker.patch 做返回值控制的三个关键参数
mocker.patch 默认返回一个 Mock 实例,但第三方接口返回的是 JSON、status_code、headers 等结构化数据,必须手动设好这些属性,否则下游解析会报错。
典型使用场景:模拟 requests.post 返回成功响应,或模拟网络超时异常。
-
return_value:设整个响应对象,比如mocker.Mock(status_code=200, json=lambda: {"id": 123}) -
side_effect:用于抛异常(如requests.exceptions.ConnectionError)或动态返回不同结果(列表或函数) - 别漏掉
text或content属性——有些旧代码不用.json(),而是直接解析.text,不设会触发AttributeError
示例:
mocker.patch('service.requests.post', return_value=mocker.Mock(
status_code=201,
json=lambda: {'token': 'abc'},
text='{"token": "abc"}'
))
测试异步接口时,patch aiohttp.ClientSession.post 报 RuntimeError
直接 patch aiohttp.ClientSession.post 会失败,因为它是绑定方法(bound method),且返回的是 ClientResponse 协程对象,mock 需要支持 await。
Python 环境工具专业版功能概述Python 环境工具专业版是一项面向实际任务的技能。它将相关步骤、工具调用和结果整理方式集中到统一流程中,帮助使用者更快完成目标并减少重复操作。核心要点使用时应结合输入条件选择合适的执行方式,核对必要参数、依赖环境与输出内容,并按原始要求处理异常情况。该技能适合需要稳定复用相关能力的场景,可作为自动化工作流的一部分,也便于后续检查、调整和扩展。执行完成后建议检查结果是否符合预期,并保留必要记录。使用与执行同时注意输入完整性、执行条件和输出可用性。从功能定位来看,该技能
正确做法是 patch 整个 session 的创建过程,或 mock 返回的 response 对象本身,并确保它可 await。
- 推荐方式:patch
aiohttp.ClientSession类,让它实例化后返回一个预设的 mock session - response mock 必须实现
__await__或用AsyncMock(Python 3.8+);pytest-mock的mocker在较新版本中已支持mocker.AsyncMock - 若用
side_effect返回协程,需用async def函数或coroutine对象,不能直接 return 普通 dict
为什么测试通过但线上仍调用真实接口
最常被忽略的一点:patch 生效范围只在装饰器/上下文管理器作用域内。如果被测函数内部又 import 了另一个模块,而那个模块里也调用了第三方接口,但你没对它 patch,就会漏掉。
还有更隐蔽的情况:日志、监控 SDK、全局钩子(如 sentry 的自动捕获)可能悄悄触发 HTTP 请求,它们不在主业务逻辑路径里,容易被遗忘。
- 打开 debug 日志,加
logging.basicConfig(level=logging.DEBUG),看测试运行时是否真有POST或GET输出 - 用
mocker.patch('requests.Session.request')统一拦截所有 requests 底层调用,比 patchget/post更彻底 - CI 环境里注意是否加载了真实配置(如环境变量
API_BASE_URL没被覆盖),导致 mock 路径和实际请求 URL 不匹配
复杂点永远在路径判断和调用链深度上——多一层 import,就多一个可能漏掉的 patch 点。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










