vcr-http-recording

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

VCR.py HTTP Recording

VCR.py HTTP录制

Record and replay HTTP interactions for Python tests.
为Python测试录制并重放HTTP交互。

Basic Setup

基础配置

python
undefined
python
undefined

conftest.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"], }
undefined
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"], }
undefined

Basic 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 == 200
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 == 200

Async 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}
ModeBehavior
once
Record if missing, then replay
new_episodes
Record new, replay existing
none
Never record (CI)
all
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}
模式行为
once
无录制文件时录制,之后重放
new_episodes
录制新请求,重放已有请求的响应
none
绝不录制(适用于CI环境)
all
总是录制(刷新响应数据)

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
undefined
yaml
undefined

tests/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
undefined
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
undefined

Key Decisions

关键决策建议

DecisionRecommendation
Record mode
once
for dev,
none
for CI
Cassette formatYAML (readable)
Sensitive dataAlways filter headers/body
Custom matchersUse for LLM APIs
决策建议
录制模式开发环境用
once
,CI环境用
none
录制文件格式YAML(易读性强)
敏感数据处理始终过滤请求头/请求体中的敏感信息
自定义匹配器针对LLM API使用

Common Mistakes

常见错误

  • Committing cassettes with real API keys
  • Using
    all
    mode in CI (makes live calls)
  • Not filtering sensitive data
  • Missing cassettes in git
  • 提交包含真实API密钥的录制文件
  • 在CI环境中使用
    all
    模式(会发起真实请求)
  • 未过滤敏感数据
  • Git中遗漏录制文件

Related Skills

相关技能

  • msw-mocking
    - Frontend equivalent
  • integration-testing
    - API testing patterns
  • llm-testing
    - LLM-specific patterns
  • msw-mocking
    - 前端等价工具
  • integration-testing
    - API测试模式
  • llm-testing
    - LLM专属测试模式

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调用费用