vcr-http-recording
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseVCR.py HTTP Recording
VCR.py HTTP录制
Record and replay HTTP interactions for Python tests.
为Python测试录制并重放HTTP交互。
Basic Setup
基础配置
python
undefinedpython
undefinedconftest.py
conftest.py
import pytest
@pytest.fixture(scope="module")
def vcr_config():
return {
"cassette_library_dir": "tests/cassettes",
"record_mode": "once",
"match_on": ["uri", "method"],
"filter_headers": ["authorization", "x-api-key"],
"filter_query_parameters": ["api_key", "token"],
}
undefinedimport pytest
@pytest.fixture(scope="module")
def vcr_config():
return {
"cassette_library_dir": "tests/cassettes",
"record_mode": "once",
"match_on": ["uri", "method"],
"filter_headers": ["authorization", "x-api-key"],
"filter_query_parameters": ["api_key", "token"],
}
undefinedBasic Usage
基础用法
python
import pytest
@pytest.mark.vcr()
def test_fetch_user():
response = requests.get("https://api.example.com/users/1")
assert response.status_code == 200
assert response.json()["name"] == "John Doe"
@pytest.mark.vcr("custom_cassette.yaml")
def test_with_custom_cassette():
response = requests.get("https://api.example.com/data")
assert response.status_code == 200python
import pytest
@pytest.mark.vcr()
def test_fetch_user():
response = requests.get("https://api.example.com/users/1")
assert response.status_code == 200
assert response.json()["name"] == "John Doe"
@pytest.mark.vcr("custom_cassette.yaml")
def test_with_custom_cassette():
response = requests.get("https://api.example.com/data")
assert response.status_code == 200Async Support
异步支持
python
import pytest
from httpx import AsyncClient
@pytest.mark.asyncio
@pytest.mark.vcr()
async def test_async_api_call():
async with AsyncClient() as client:
response = await client.get("https://api.example.com/data")
assert response.status_code == 200
assert "items" in response.json()python
import pytest
from httpx import AsyncClient
@pytest.mark.asyncio
@pytest.mark.vcr()
async def test_async_api_call():
async with AsyncClient() as client:
response = await client.get("https://api.example.com/data")
assert response.status_code == 200
assert "items" in response.json()Recording Modes
录制模式
python
@pytest.fixture(scope="module")
def vcr_config():
import os
# CI: never record, only replay
if os.environ.get("CI"):
record_mode = "none"
else:
record_mode = "new_episodes"
return {"record_mode": record_mode}| Mode | Behavior |
|---|---|
| Record if missing, then replay |
| Record new, replay existing |
| Never record (CI) |
| Always record (refresh) |
python
@pytest.fixture(scope="module")
def vcr_config():
import os
# CI: 绝不录制,仅重放
if os.environ.get("CI"):
record_mode = "none"
else:
record_mode = "new_episodes"
return {"record_mode": record_mode}| 模式 | 行为 |
|---|---|
| 无录制文件时录制,之后重放 |
| 录制新请求,重放已有请求的响应 |
| 绝不录制(适用于CI环境) |
| 总是录制(刷新响应数据) |
Filtering Sensitive Data
敏感数据过滤
python
def filter_request_body(request):
"""Redact sensitive data from request body."""
import json
if request.body:
try:
body = json.loads(request.body)
if "password" in body:
body["password"] = "REDACTED"
if "api_key" in body:
body["api_key"] = "REDACTED"
request.body = json.dumps(body)
except json.JSONDecodeError:
pass
return request
@pytest.fixture(scope="module")
def vcr_config():
return {
"filter_headers": ["authorization", "x-api-key"],
"before_record_request": filter_request_body,
}python
def filter_request_body(request):
"""从请求体中脱敏敏感数据。"""
import json
if request.body:
try:
body = json.loads(request.body)
if "password" in body:
body["password"] = "REDACTED"
if "api_key" in body:
body["api_key"] = "REDACTED"
request.body = json.dumps(body)
except json.JSONDecodeError:
pass
return request
@pytest.fixture(scope="module")
def vcr_config():
return {
"filter_headers": ["authorization", "x-api-key"],
"before_record_request": filter_request_body,
}LLM API Testing
LLM API测试
python
def llm_request_matcher(r1, r2):
"""Match LLM requests ignoring dynamic fields."""
import json
if r1.uri != r2.uri or r1.method != r2.method:
return False
body1 = json.loads(r1.body)
body2 = json.loads(r2.body)
# Ignore dynamic fields
for field in ["request_id", "timestamp"]:
body1.pop(field, None)
body2.pop(field, None)
return body1 == body2
@pytest.fixture(scope="module")
def vcr_config():
return {
"custom_matchers": [llm_request_matcher],
}python
def llm_request_matcher(r1, r2):
"""忽略动态字段匹配LLM请求。"""
import json
if r1.uri != r2.uri or r1.method != r2.method:
return False
body1 = json.loads(r1.body)
body2 = json.loads(r2.body)
# 忽略动态字段
for field in ["request_id", "timestamp"]:
body1.pop(field, None)
body2.pop(field, None)
return body1 == body2
@pytest.fixture(scope="module")
def vcr_config():
return {
"custom_matchers": [llm_request_matcher],
}Cassette File Example
录制文件示例
yaml
undefinedyaml
undefinedtests/cassettes/test_fetch_user.yaml
tests/cassettes/test_fetch_user.yaml
interactions:
- request: body: null headers: Content-Type: application/json method: GET uri: https://api.example.com/users/1 response: body: string: '{"id": 1, "name": "John Doe"}' status: code: 200 version: 1
undefinedinteractions:
- request: body: null headers: Content-Type: application/json method: GET uri: https://api.example.com/users/1 response: body: string: '{"id": 1, "name": "John Doe"}' status: code: 200 version: 1
undefinedKey Decisions
关键决策建议
| Decision | Recommendation |
|---|---|
| Record mode | |
| Cassette format | YAML (readable) |
| Sensitive data | Always filter headers/body |
| Custom matchers | Use for LLM APIs |
| 决策 | 建议 |
|---|---|
| 录制模式 | 开发环境用 |
| 录制文件格式 | YAML(易读性强) |
| 敏感数据处理 | 始终过滤请求头/请求体中的敏感信息 |
| 自定义匹配器 | 针对LLM API使用 |
Common Mistakes
常见错误
- Committing cassettes with real API keys
- Using mode in CI (makes live calls)
all - Not filtering sensitive data
- Missing cassettes in git
- 提交包含真实API密钥的录制文件
- 在CI环境中使用模式(会发起真实请求)
all - 未过滤敏感数据
- Git中遗漏录制文件
Related Skills
相关技能
- - Frontend equivalent
msw-mocking - - API testing patterns
integration-testing - - LLM-specific patterns
llm-testing
- - 前端等价工具
msw-mocking - - API测试模式
integration-testing - - LLM专属测试模式
llm-testing
Capability Details
功能详情
http-recording
http-recording
Keywords: record HTTP, vcr.use_cassette, record mode, capture HTTP
Solves:
- Record HTTP interactions for replay
- Capture real API responses
- Create deterministic test fixtures
关键词: record HTTP, vcr.use_cassette, record mode, capture HTTP
解决的问题:
- 录制HTTP交互以便重放
- 捕获真实API响应
- 创建可预测的测试固定数据
cassette-replay
cassette-replay
Keywords: replay, cassette, playback, mock replay
Solves:
- Replay recorded HTTP interactions
- Run tests without network access
- Ensure consistent test results
关键词: replay, cassette, playback, mock replay
解决的问题:
- 重放已录制的HTTP交互
- 无需网络即可运行测试
- 确保测试结果一致
async-support
async-support
Keywords: async, aiohttp, httpx async, async cassette
Solves:
- Record async HTTP clients
- Handle aiohttp and httpx async
- Test async API integrations
关键词: async, aiohttp, httpx async, async cassette
解决的问题:
- 录制异步HTTP客户端请求
- 支持aiohttp和httpx异步请求
- 测试异步API集成
sensitive-data-filtering
sensitive-data-filtering
Keywords: filter, scrub, redact, sensitive data, before_record
Solves:
- Scrub API keys from cassettes
- Redact sensitive data
- Implement before_record hooks
关键词: filter, scrub, redact, sensitive data, before_record
解决的问题:
- 从录制文件中清除API密钥
- 脱敏敏感数据
- 实现before_record钩子函数
custom-matchers
custom-matchers
Keywords: matcher, match on, request matching, custom match
Solves:
- Configure request matching rules
- Ignore dynamic request parts
- Match by method/host/path
关键词: matcher, match on, request matching, custom match
解决的问题:
- 配置请求匹配规则
- 忽略请求中的动态部分
- 按方法/主机/路径匹配请求
llm-api-testing
llm-api-testing
Keywords: LLM cassette, OpenAI recording, Anthropic recording
Solves:
- Record LLM API responses
- Test AI integrations deterministically
- Avoid costly API calls in tests
关键词: LLM cassette, OpenAI recording, Anthropic recording
解决的问题:
- 录制LLM API响应
- 确定性地测试AI集成
- 避免测试中产生高昂的API调用费用