根本原因是请求未被真正录制或回放时匹配失败;vcrpy默认只拦截requests,对httpx、urllib等无效,且URL参数、headers、body严格匹配易导致fallback至真实请求。

为什么 pytest-vcr 录制后回放总是报 404 或连接拒绝?
根本原因通常是请求没被真正录制,或回放时匹配逻辑失败。pytest-vcr 默认只拦截 requests(不含 httpx、urllib 原生调用),且对 URL 查询参数、headers、body 的匹配是严格默认开启的。如果你用的是 httpx 或手动构造了 urllib.request.Request,vcrpy 根本看不到请求,自然不会存 cassette,回放时就直连真实服务——此时若服务不可达或地址变了,就会报连接错误或 404。
实操建议:
立即学习“Python免费学习笔记(深入)”;
安全 Python 代码沙箱:在隔离的沙箱中执行任意 Python 代码,预装 requests、NumPy、pandas 库。返回 stdout、stderr。
- 确认 HTTP 客户端:只对
requests开箱即用;用httpx需额外装pytest-httpx或改用vcrpy的register_matcher+ 自定义适配器 - 检查 cassette 文件是否生成:运行测试加
--vcr-record=once后,看cassettes/目录下是否有对应 YAML 文件;没有就说明没触发录制 - 临时关闭严格匹配:在
@vcr.use_cassette中加match_on=["method", "uri"],排除 headers/body 干扰 - 避免动态参数:URL 中带时间戳、随机 UUID、token 会导致每次请求 URI 不同,匹配失败;用
filter_query_parameters清洗掉
如何正确配置 pytest-vcr 的 conftest.py 和 fixture?
不靠全局 pytest_configure 硬编码路径,而是用 fixture 统一管理 cassette 生命周期,更可控、易测、可复用。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 在项目根目录下建
conftest.py,内容包含:
import pytest
import vcr
<p>@pytest.fixture
def vcr_cassette_dir(request):</p><h1>把 cassette 存到 tests/cassettes/ 下,按模块+函数名组织</h1><pre class="brush:php;toolbar:false;">return f"tests/cassettes/{request.module.__name__}"@pytest.fixture def vcr_config(): return { "record_mode": "once", "cassette_library_dir": "tests/cassettes", "filter_query_parameters": ["api_key", "timestamp", "signature"], "ignore_localhost": True, "decode_compressed_response": True, }
- 测试文件里直接用
@pytest.mark.vcr(无需参数)即可自动读取配置和路径 - 如需单测覆盖不同行为,可在测试函数上加
@pytest.mark.vcr(record_mode="all")强制重录
录制时遇到 SSL 验证失败或证书警告怎么办?
不是代理问题,也不是 pytest-vcr 的 bug,而是 vcrpy 在录制阶段仍会走真实 HTTPS 握手——如果目标站点证书异常(如自签名、过期)、或系统缺少根证书(常见于 Docker Alpine 镜像),就会抛 SSLError 或警告,导致录制中断。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 开发环境可临时禁用验证(仅限录制):在
vcr_config中加"custom_patches": [("requests.adapters.HTTPAdapter.send", lambda *a, **kw: setattr(kw, "verify", False) or kw)]——但更推荐下面方式 - 稳妥做法:用
pytest-vcr的allow_hosts+ignore_hosts控制范围,配合系统级证书修复(如apk add ca-certificates) - 绝对不要在回放阶段关 SSL 验证:回放本不该发真实请求,关了反而掩盖了“本该走 cassette 却直连”的 bug
回放失败但 cassette 明明存在?重点查这三处
YAML 文件存在 ≠ 能成功匹配。vcrpy 匹配失败时静默 fallback 到真实请求,你看到的错误其实是下游服务返回的,不是 vcr 的报错。
排查顺序:
- 打开 cassette 文件,确认
interactions数量 ≥ 1,且request.method和request.uri与代码中一致(注意 trailing slash、大小写、编码) - 在测试中加
print(vcr.get_active_cassette().play_count),回放后为 0 就说明完全没匹配上 - 启用 debug 日志:
export VCR_LOG_LEVEL=DEBUG,运行测试看日志里是否出现Match failed for request...及具体 mismatch 字段
最常被忽略的是 body 内容差异:POST JSON 中字段顺序、空格、换行符、浮点数精度都可能导致哈希不等。用 match_on=["method", "uri", "body"] 前先确认 body 确实稳定;否则建议过滤掉非关键字段或用 before_record_request 归一化。

















