pytest通过responses库模拟http请求:安装后用@responses.activate装饰器和responses.add()注册预设响应,拦截requests调用,无需启动真实服务;异步场景则用respx替代。

pytest怎么模拟HTTP请求而不启动真实服务
微服务测试的核心是隔离依赖,避免每次跑测试都得把所有下游服务拉起来。直接用 requests 调真实地址会导致测试不稳定、慢、环境强依赖——这不是单元测试,是集成测试的负担。
推荐用 responses 库做 HTTP 层 mock,它能拦截 requests 发出的任何请求,并返回预设响应,无需改业务代码中的 requests.get() 或 requests.post() 调用。
- 安装:
pip install responses - 在测试函数里用
@responses.activate装饰器开启拦截 - 用
responses.add()注册期望的 URL、method、status、body(JSON 字符串或字典) - 确保被测代码确实走的是
requests(不是httpx或aiohttp,那得换对应 mock 方案)
示例:
@responses.activate
def test_get_user_from_auth_service():
responses.add(
responses.GET,
"https://auth-service/users/123",
json={"id": 123, "name": "alice"},
status=200
)
result = fetch_user_from_auth(123) # 内部调用 requests.get(...)
assert result["name"] == "alice"
如何验证微服务间调用的参数和头信息是否正确
光看返回值不够,下游服务可能因 Authorization 头缺失、Content-Type 错误、或 query 参数拼错而静默失败。pytest 测试里得检查请求是否“发得对”,而不是只信响应。
responses 支持回调模式:用 responses.CallbackResponse 拿到原始 request 对象,断言其 headers、params、body 等字段。
- 回调函数必须返回三元组:
(status, headers, body) - 在回调里加
assert,比如assert request.headers.get("Authorization") == "Bearer xyz" - 注意:如果业务代码用了
json=...参数,request.body是 bytes,需用json.loads(request.body.decode())解析
测试异步微服务调用(如 httpx + asyncio)该用什么工具
若你的服务用 httpx.AsyncClient 发请求,responses 无效——它只 hook 同步 requests。此时得换 respx,它是专为 httpx 设计的 mock 工具,支持同步/异步双模式。
- 安装:
pip install respx - 异步测试要加
async def和await,并用@respx.mock - 路由匹配更灵活:支持正则、host、path、甚至 query 参数校验
- 别忘了在 pytest 配置里启用 asyncio 支持(
asyncio_mode = autoinpytest.ini)
示例:
@respx.mock
async def test_async_payment_call():
route = respx.post("https://payment-service/charge").mock(
return_value=httpx.Response(201, json={"tx_id": "abc"})
)
await charge_user("user_456", 99.9)
assert route.called
assert route.call_count == 1
为什么测试里不能直接 patch requests.request
有人图省事用 unittest.mock.patch("requests.request"),这会掩盖真实问题:比如你调用的是 session.get(),但 patch 的是顶层 requests.request,结果 mock 不生效;或者下游库内部用了 urllib3 直接发包,绕过 requests API。
更危险的是,这种 patch 容易漏掉重试逻辑、SSL 配置、超时设置等真实行为,导致测试通过但线上报 ConnectionError 或 Timeout。
-
responses和respx是协议层拦截,覆盖所有基于requests/httpx的调用路径 - 它们保留了连接池、重试、重定向等中间行为,mock 更贴近真实
- 一旦发现 mock 不生效,优先检查是否用了其他 HTTP 客户端(如
urllib.request),而不是硬 patch
真实微服务调用链越深,越要守住“只 mock 直接依赖”这条线——下游的下游,让它自己测。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











