0

0

掌握 requests-mock:高效模拟动态 URL 请求与响应序列

DDD

DDD

发布时间:2025-08-14 22:28:18

|

373人浏览过

|

来源于php中文网

原创

掌握 requests-mock:高效模拟动态 URL 请求与响应序列

本教程深入探讨如何使用 requests-mock 库高效模拟 Python 中 requests 库发起的 HTTP 请求,特别是针对包含动态参数(如页码)的 URL。文章将详细介绍如何利用正则表达式匹配 URL,并通过 side_effect 参数模拟一系列不同的响应,从而在测试环境中精确控制外部 API 调用的行为,确保测试的隔离性和可靠性。

在开发与外部 api 交互的 python 应用时,进行单元测试和集成测试至关重要。然而,直接调用外部 api 不仅会增加测试时间、消耗网络资源,还可能受到 api 速率限制或不可预测的网络波动影响,导致测试结果不稳定。为了解决这些问题,模拟(mocking)外部 http 请求成为一种标准实践。

本教程将聚焦于如何使用 requests-mock 库来模拟 requests 库的 HTTP 请求。我们将特别关注处理动态 URL 的场景,例如分页 API 调用,并演示如何精确控制模拟响应的序列,以确保测试逻辑的完整性。

为什么需要模拟 HTTP 请求?

  1. 隔离性: 将被测代码与外部依赖(如网络、数据库)隔离,确保测试只关注代码本身的逻辑。
  2. 速度: 避免实际的网络延迟,显著加快测试执行速度。
  3. 可靠性: 消除外部服务不可用、网络波动等不确定因素对测试结果的影响。
  4. 场景模拟: 轻松模拟各种成功、失败、异常或特定数据返回的场景,这些场景在真实环境中可能难以复现。

引入 requests-mock

requests-mock 是一个功能强大且易于使用的 Python 库,专门用于模拟 requests 库发出的 HTTP 请求。它通过在 requests 内部注册适配器来实现请求的拦截和响应的模拟,而无需修改任何应用代码。

首先,确保你已经安装了 requests-mock:

pip install requests-mock

核心概念:URL 匹配与正则表达式

在许多 API 调用场景中,URL 并非固定不变,而是包含动态参数,例如分页 API 的页码 (?page=1, ?page=2)。requests-mock 允许使用正则表达式来匹配这类动态 URL,从而用一个规则覆盖多个可能的请求。

考虑以下用于从 SWAPI(星球大战 API)获取人物列表的函数:

import requests
from http import HTTPStatus

def consuming_api_swapi_index_page(initial_page: int = 1):
    """Swapi index page."""
    check = HTTPStatus.OK
    results = []
    current_page = initial_page
    while check == HTTPStatus.OK:
        response = requests.get(
            f'https://swapi.dev/api/people/?page={current_page}'
        )
        # 这里的 response.url 是关键,它会是实际请求的 URL
        results.append(url := response.url)
        print(url)
        check = response.status_code
        current_page += 1
    return results

这个函数会循环调用 API,直到收到非 200 的状态码。在测试中,我们需要模拟一系列请求,每个请求的 URL 中的 page 参数都会递增。

我们可以使用 re.compile 来创建一个正则表达式模式,以匹配所有页码的 URL:

import re
import requests_mock

# 定义匹配所有页码的正则表达式
matcher = re.compile(r"https://swapi.dev/api/people/\?page=\d+")

# 在测试中,requests-mock 会拦截所有匹配此模式的 GET 请求
# m是一个requests_mock.Mocker对象,通常通过pytest的fixture提供
# m.register_uri('GET', matcher, text='Mocked Response')

关键点: 当 requests-mock 拦截到一个匹配 matcher 的请求时,它会使用你提供的模拟响应。更重要的是,response.url 属性会自动设置为实际发起请求的 URL,而不是匹配模式本身。这意味着如果请求的是 https://swapi.dev/api/people/?page=1,那么 response.url 就会是 https://swapi.dev/api/people/?page=1,这正是我们函数逻辑所需要的。

GentleAI
GentleAI

GentleAI是一个高效的AI工作平台,为普通人提供智能计算、简单易用的界面和专业技术支持。让人工智能服务每一个人。

下载

模拟动态响应序列:利用 side_effect 控制流程

对于像 consuming_api_swapi_index_page 这样依赖于连续请求状态(如循环终止条件)的函数,仅仅提供一个静态响应是不够的。我们需要模拟一系列不同的响应,例如前几页返回成功状态,最后一页返回非成功状态以终止循环。

requests-mock 提供了 side_effect 参数,它允许你提供一个响应字典列表。每次匹配到请求时,requests-mock 会按顺序从列表中取出一个响应来返回。当列表耗尽后,后续的请求将不再被模拟(除非有其他规则匹配)。

为了让上述 consuming_api_swapi_index_page 函数在测试中正确执行并终止,我们需要模拟 10 个成功的请求(对应页码 1 到 10),然后模拟一个非 200 的请求来结束循环。

以下是一个完整的测试案例:

import requests
import requests_mock
import re
from http import HTTPStatus

# 假设 consuming_api_swapi_index_page 函数已定义如上

def test_consuming_api_swapi_index_page(requests_mock) -> None:
    """Test it."""
    # 期望的 URL 列表
    expected_urls = [
        'https://swapi.dev/api/people/?page=1',
        'https://swapi.dev/api/people/?page=2',
        'https://swapi.dev/api/people/?page=3',
        'https://swapi.dev/api/people/?page=4',
        'https://swapi.dev/api/people/?page=5',
        'https://swapi.dev/api/people/?page=6',
        'https://swapi.dev/api/people/?page=7',
        'https://swapi.dev/api/people/?page=8',
        'https://swapi.dev/api/people/?page=9',
        'https://swapi.dev/api/people/?page=10',
    ]

    # 定义匹配所有页码的正则表达式
    matcher = re.compile(r"https://swapi.dev/api/people/\?page=\d+")

    # 准备 side_effect 响应列表
    # 前10个请求返回 HTTPStatus.OK (200)
    # 第11个请求返回 HTTPStatus.NOT_FOUND (404) 以终止循环
    mock_responses = []
    for _ in range(10):
        mock_responses.append({'status_code': HTTPStatus.OK, 'json': {'results': []}}) # 模拟一个空results,如果函数需要解析json
    mock_responses.append({'status_code': HTTPStatus.NOT_FOUND}) # 终止循环的响应

    # 注册 URI,使用 side_effect 提供响应序列
    requests_mock.get(matcher, side_effect=mock_responses)

    # 调用被测试函数
    actual_urls = consuming_api_swapi_index_page()

    # 断言结果是否符合预期
    assert actual_urls == expected_urls

# 注意:如果使用 pytest,requests_mock 会作为 fixture 自动提供。
# 如果不使用 pytest,你需要手动创建 Mocker 实例并使用 with 语句:
# import requests_mock
# with requests_mock.Mocker() as m:
#     m.get(matcher, side_effect=mock_responses)
#     actual_urls = consuming_api_swapi_index_page()
#     assert actual_urls == expected_urls

在这个例子中:

  1. 我们定义了一个正则表达式 matcher 来匹配所有 page 参数的 URL。
  2. mock_responses 列表包含了 10 个成功响应(状态码 200)和一个非成功响应(状态码 404)。
  3. 当 consuming_api_swapi_index_page 函数第一次调用 requests.get 时,requests-mock 会拦截它,并返回 mock_responses 列表中的第一个响应。
  4. 函数继续循环,每次调用 requests.get 都会从 mock_responses 中取出下一个响应,直到第 11 次调用时,返回 404 状态码,从而使函数跳出循环。
  5. response.url 在每次调用中都会自动设置为实际请求的 URL(例如 https://swapi.dev/api/people/?page=1,然后是 page=2,依此类推),这完美地满足了 results.append(url := response.url) 的需求。

注意事项与最佳实践

  • 测试粒度: 尽可能在单元测试层面模拟外部依赖,保持测试的快速和独立。
  • 明确的模拟范围: 使用 requests-mock 时,确保你只模拟了你想要控制的请求。对于未注册的请求,requests-mock 默认会允许它们通过,除非你显式地禁用网络 (requests_mock.Mocker(kw='disable_net_connect=True'))。
  • 模拟所有相关属性: 除了 status_code 和 url,你可能还需要模拟 json()、text、headers 等 Response 对象的其他属性,以确保被测函数能够正常工作。
  • 错误和异常场景: 利用 side_effect 或注册不同的 URI 来模拟网络错误、超时、服务器错误等各种异常情况,以验证代码的健壮性。
  • 自定义匹配器: 对于更复杂的匹配逻辑,requests-mock 也支持自定义匹配器函数,这提供了极高的灵活性,但对于大多数动态 URL 场景,正则表达式已足够。

总结

requests-mock 是 Python 中进行 HTTP 请求模拟的强大工具。通过结合正则表达式进行灵活的 URL 匹配,并利用 side_effect 参数来模拟序列化的响应,我们可以精确地控制外部 API 调用的行为,从而编写出隔离性好、执行快速且结果可靠的测试。掌握这些技巧将显著提升你的测试效率和代码质量。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
json数据格式
json数据格式

JSON是一种轻量级的数据交换格式。本专题为大家带来json数据格式相关文章,帮助大家解决问题。

457

2023.08.07

json是什么
json是什么

JSON是一种轻量级的数据交换格式,具有简洁、易读、跨平台和语言的特点,JSON数据是通过键值对的方式进行组织,其中键是字符串,值可以是字符串、数值、布尔值、数组、对象或者null,在Web开发、数据交换和配置文件等方面得到广泛应用。本专题为大家提供json相关的文章、下载、课程内容,供大家免费下载体验。

549

2023.08.23

jquery怎么操作json
jquery怎么操作json

操作的方法有:1、“$.parseJSON(jsonString)”2、“$.getJSON(url, data, success)”;3、“$.each(obj, callback)”;4、“$.ajax()”。更多jquery怎么操作json的详细内容,可以访问本专题下面的文章。

337

2023.10.13

go语言处理json数据方法
go语言处理json数据方法

本专题整合了go语言中处理json数据方法,阅读专题下面的文章了解更多详细内容。

82

2025.09.10

js正则表达式
js正则表达式

php中文网为大家提供各种js正则表达式语法大全以及各种js正则表达式使用的方法,还有更多js正则表达式的相关文章、相关下载、相关课程,供大家免费下载体验。

531

2023.06.20

正则表达式不包含
正则表达式不包含

正则表达式,又称规则表达式,,是一种文本模式,包括普通字符和特殊字符,是计算机科学的一个概念。正则表达式使用单个字符串来描述、匹配一系列匹配某个句法规则的字符串,通常被用来检索、替换那些符合某个模式的文本。php中文网给大家带来了有关正则表达式的相关教程以及文章,希望对大家能有所帮助。

258

2023.07.05

java正则表达式语法
java正则表达式语法

java正则表达式语法是一种模式匹配工具,它非常有用,可以在处理文本和字符串时快速地查找、替换、验证和提取特定的模式和数据。本专题提供java正则表达式语法的相关文章、下载和专题,供大家免费下载体验。

766

2023.07.05

java正则表达式匹配字符串
java正则表达式匹配字符串

在Java中,我们可以使用正则表达式来匹配字符串。本专题为大家带来java正则表达式匹配字符串的相关内容,帮助大家解决问题。

219

2023.08.11

TypeScript类型系统进阶与大型前端项目实践
TypeScript类型系统进阶与大型前端项目实践

本专题围绕 TypeScript 在大型前端项目中的应用展开,深入讲解类型系统设计与工程化开发方法。内容包括泛型与高级类型、类型推断机制、声明文件编写、模块化结构设计以及代码规范管理。通过真实项目案例分析,帮助开发者构建类型安全、结构清晰、易维护的前端工程体系,提高团队协作效率与代码质量。

26

2026.03.13

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
最新Python教程 从入门到精通
最新Python教程 从入门到精通

共4课时 | 22.5万人学习

Django 教程
Django 教程

共28课时 | 5万人学习

SciPy 教程
SciPy 教程

共10课时 | 1.9万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号