如何monkeypatch requests库保存测试过程中的请求响应
实现自动录制+Mock请求的方案
你不需要逐个monkeypatch每个系统类实例里的session对象,直接在类层面补丁requests.Session的核心请求方法即可:所有requests.Session实例的get/post/put等上层方法,底层最终都会调用实例的request方法,在类层面打补丁会自动覆盖所有已创建、后续新创建的session实例,不管这些session属于哪个封装类,后续新增多少系统类都不需要额外适配。
具体实现
把下面的fixture放到测试目录的conftest.py中,自动兼容录制、Mock两种模式:
import pytest import requests import json from pathlib import Path from unittest.mock import patch # 录制文件存储路径 RECORD_SAVE_PATH = Path("./test_mock_records/request_caches.json") @pytest.fixture(scope="session", autouse=True) def auto_record_and_mock(): # 已存在录制文件时,自动加载规则走Mock模式 if RECORD_SAVE_PATH.exists(): import responses with responses.RequestsMock(assert_all_requests_are_fired=False) as rsps: with open(RECORD_SAVE_PATH, "r", encoding="utf-8") as f: all_records = json.load(f) # 批量注册Mock响应 for record in all_records: rsps.add( method=record["method"], url=record["url"], body=record["resp_body"], status=record["resp_status"], headers=record["resp_headers"], match=[responses.matchers.urlencoded_params_matcher(record["req_data"])] if record["req_data"] else [] ) yield return # 无录制文件时走录制模式,拦截请求存真实响应 request_records = [] origin_session_request = requests.Session.request def wrapped_request(self, method, url, **kwargs): # 发送真实请求获取响应 resp = origin_session_request(self, method, url, **kwargs) # 存储请求、响应全量信息 request_records.append({ "method": method.upper(), "url": url, "req_data": kwargs.get("data"), "req_json": kwargs.get("json"), "resp_status": resp.status_code, "resp_headers": dict(resp.headers), "resp_body": resp.text }) return resp # 全局补丁所有Session实例的request方法 with patch.object(requests.Session, "request", wrapped_request): yield # 所有测试执行完成后,保存录制结果到本地 RECORD_SAVE_PATH.parent.mkdir(exist_ok=True) with open(RECORD_SAVE_PATH, "w", encoding="utf-8") as f: json.dump(request_records, f, ensure_ascii=False, indent=2)
使用说明
- 首次录制:删除已存在的录制文件,保证测试环境可以正常访问外部系统,完整执行一次全量测试,执行完成后所有请求、响应数据会自动保存到指定路径
- 日常测试:只要录制文件存在,fixture会自动通过
responses注册所有Mock规则,测试运行全程不会发送真实外部请求,完全不依赖外部服务可用性 - 接口更新/新增接口时,重复首次录制的流程重新生成录制文件即可,不需要手动维护任何Mock规则
适配调整提示
- 如果接口存在动态参数(比如时间戳、随机nonce、动态token),可以在加载Mock规则时替换成自定义匹配器,不需要硬匹配全量参数
- 如果存在文件下载等二进制响应场景,存储时可以把响应体转base64编码,加载Mock时再解码即可
- fixture加了
autouse=True,测试文件里不需要手动引用这个fixture,自动生效
内容的提问来源于stack exchange,提问作者sourcream
相关产品推荐
相关产品推荐

