推荐用requests_mock全局拦截requests调用,它通过patch requests.session.request实现轻量级、非侵入式模拟,支持url/method/headers/json匹配,但仅限requests库且不适用于多线程/协程场景。

用 requests_mock 全局拦截所有 requests 调用
如果你只用 requests 发请求,requests_mock 是最轻量、最直接的方案。它通过 monkey patch requests.Session.request 实现全局拦截,无需改业务代码。
安装后,在测试入口或 setUp 里启用即可:
import requests_mock
with requests_mock.Mocker() as m:
m.get('https://api.example.com/users', json=[{'id': 1}])
resp = requests.get('https://api.example.com/users')
assert resp.json() == [{'id': 1}]
注意:它默认只 mock 当前线程内的 requests 调用;多线程/协程需额外处理(见下一条)。
- mock 范围仅限
requests库,对urllib、httpx、aiohttp无效 - 若使用了自定义
Session实例,要确保没禁用mount或绕过默认 adapter - 不推荐在生产环境启用——它会静默覆盖真实网络行为,且无 warning
用 pytest-httpx 拦截 httpx 请求(含异步)
当项目迁移到 httpx(尤其用 AsyncClient),requests_mock 失效。pytest-httpx 是专为 httpx 设计的 fixture 方案,天然支持 sync/async 混合场景。
只需在测试函数参数中声明 httpx_mock,它会自动接管所有 httpx.Client 和 httpx.AsyncClient 实例:
def test_fetch_user(httpx_mock):
httpx_mock.add_response(url='https://api.example.com/user/1', json={'name': 'alice'})
resp = httpx.get('https://api.example.com/user/1')
assert resp.json()['name'] == 'alice'
@pytest.mark.asyncio
async def test_fetch_user_async(httpx_mock):
httpx_mock.add_response(url='https://api.example.com/user/1', json={'name': 'alice'})
async with httpx.AsyncClient() as client:
resp = await client.get('https://api.example.com/user/1')
assert resp.json()['name'] == 'alice'
关键点:
- 必须用 pytest 运行,不能直接用
python -m unittest - mock 规则按添加顺序匹配,遇到第一个匹配即返回,不继续遍历
- 若请求未被 mock,
httpx_mock默认抛出httpx.TimeoutException(可配成透传)
用 responses 拦截底层 socket(覆盖 urllib / requests / httpx)
当你需要统一拦截所有基于 socket 的 HTTP 流量(比如某些 SDK 内部用 urllib.request,或你用了多个 HTTP 库),responses 更底层——它 patch 了 urllib3.util.connection.create_connection 和 socket.socket。
启用方式类似:
import responses
@responses.activate
def test_with_urllib():
responses.add(responses.GET, 'https://api.example.com/data', json={'ok': True})
import urllib.request
with urllib.request.urlopen('https://api.example.com/data') as f:
assert f.read() == b'{"ok": true}'
但要注意:
- 它对 HTTPS 请求依赖于证书验证逻辑,某些自签名或跳过验证的场景可能绕过 mock
- 无法拦截纯 TCP 连接(如 Redis、PostgreSQL),只管 HTTP/HTTPS 协议栈
- 与
requests_mock同时启用会冲突,选一个即可
为什么不用 unittest.mock.patch 直接 patch?
手动 patch requests.get 或 httpx.Client.request 看似简单,但实际容易漏掉几个关键点:
- 业务代码可能调用
session.post()、client.put()、requests.request(method=...),patch 单个函数不够 - 如果用了连接池(
requests.adapters.HTTPAdapter)、重试策略、超时设置,mock 后这些逻辑全失效,行为失真 - 异步函数里 patch
httpx.AsyncClient需要asyncio.run配合,容易引发 event loop 错误 - mock 返回对象若不是真实
Response实例(比如用Mock()),可能触发.raise_for_status()或.json()异常
真正难的不是“怎么 mock”,而是让 mock 行为和真实请求在状态、异常、生命周期上保持一致——这也是为什么推荐用专库而非裸 patch。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











