广告
您当前的位置: 首页 >  技术 >  Python

Python 深入浅出:用 responses 与 aioresponses 优雅 Mock 外部 HTTP 接口

作者:XiaoZhang 时间:2026-06-28 阅读数:5人阅读

我们已经知道,在编写单元测试时,必须通过 Mock 机制阻断和模拟真实的外部网络请求(如调用第三方支付接口、短信网关或外部大模型 API)。

但是,如果直接使用 Python 标准库的 unittest.mock.patch 去模拟网络库(如 requestsaiohttp),代码会变得非常繁琐且脆弱:你不得不深入伪装 requests.get().json().get() 等一连串的方法调用,导致测试代码与网络库的内部调用实现深度耦合。

为了更优雅、声明式地模拟网络流量,Python 生态中诞生了专门针对 HTTP 请求的 Mock 神器:responses(针对同步库 requests) 与 aioresponses(针对异步库 aiohttp)。

本文将带您掌握这两款专用测试工具,写出更整洁、更健壮的单元测试。


一、 传统 Mock 的痛点

传统的 patch 方式往往需要层层包装:

# 过于繁琐且容易因为 requests 链式调用改变而失效
with patch("requests.get") as mock_get:
    mock_get.return_value.status_code = 200
    mock_get.return_value.json.return_value = {"status": "ok"}

这种写法的本质是在模拟“Python 对象的行为”。而我们真正想模拟的,其实是“网络层面的 HTTP 交互行为”(即:当向 URL A 发送 GET 请求时,网络应答返回 JSON B 和状态码 200)。


二、 声明式模拟 requests 请求:responses

responses 库通过在底层拦截 requests 发出的 Socket 流量,允许我们以声明式的方式直接定义网络层行为。

安装方式:

pip install responses

代码实战:使用 @responses.activate

import requests
import responses

def fetch_user_score(user_id):
    # 一个普通的 HTTP 请求函数
    response = requests.get(f"https://api.example.com/users/{user_id}/score")
    if response.status_code == 200:
        return response.json()["score"]
    return 0

# 使用装饰器激活拦截器
@responses.activate
def test_fetch_user_score():
    target_url = "https://api.example.com/users/42/score"

    # 声明式添加一个模拟规则:当拦截到向 target_url 的 GET 请求时,直接返回指定 JSON 和 200
    responses.add(
        method=responses.GET,
        url=target_url,
        json={"score": 98},
        status=200
    )

    # 执行我们的业务函数
    score = fetch_user_score(42)

    # 验证逻辑
    assert score == 98

这样的测试代码不需要关心 requests 底层是怎么实现的,只关注网络输入输出契约,非常直观且易于维护。


三、 声明式模拟异步 aiohttp 请求:aioresponses

在异步协程开发中,我们常用 aiohttp 进行高并发 HTTP 调用。responses 无法拦截异步流量,我们需要使用专门针对 aiohttpaioresponses

安装方式:

pip install aioresponses

代码实战:异步 Mock 示例

import aiohttp
from aioresponses import aioresponses
import asyncio

async def fetch_async_data(session, url):
    async with session.get(url) as response:
        return await response.json()

async def test_async_fetch():
    # 使用 contextmanager 激活异步流量拦截
    with aioresponses() as m:
        target_url = "https://api.example.com/data"

        # 声明式模拟异步 GET 请求的响应载荷 (payload)
        m.get(target_url, payload={"status": "success", "value": 100})

        async with aiohttp.ClientSession() as session:
            result = await fetch_async_data(session, target_url)

            # 验证结果
            assert result["value"] == 100
            assert result["status"] == "success"

if __name__ == "__main__":
    asyncio.run(test_async_fetch())

四、 总结

  1. 摆脱底层对象伪装:告别繁琐的 return_value 链条,改用针对网络流量的声明式 Mock。
  2. 同步异步分工明确
  3. 在测试同步的 requests 时,使用 responses
  4. 在测试异步的 aiohttp 时,使用 aioresponses
  5. 保障网络绝对隔离:这两款库都提供安全开关,在测试激活期间,任何未被显式 mock 的网络请求都会被直接拒绝并抛出异常,防止单元测试误触真实线上网络接口。

用正确专业的工具做网络隔离,您的测试防护网将会变得更加优雅、稳固与整洁!

本站所有文章、数据、图片均来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。

评论交流 (0)

正在加载评论...
头像

XiaoZhang

当你还撑不起你的梦想时,就要去奋斗。如果缘分安排我们相遇,请不要让她擦肩和过。我们一起奋斗!

微信