vcr-http-recording
VCR.py HTTP recording for Python tests. Use when testing Python code making HTTP requests, recording API responses for replay, or creating deterministic tests for external services.
$ 安裝
git clone https://github.com/yonatangross/skillforge-claude-plugin /tmp/skillforge-claude-plugin && cp -r /tmp/skillforge-claude-plugin/.claude/skills/vcr-http-recording ~/.claude/skills/skillforge-claude-plugin// tip: Run this command in your terminal to install the skill
SKILL.md
name: vcr-http-recording description: VCR.py HTTP recording for Python tests. Use when testing Python code making HTTP requests, recording API responses for replay, or creating deterministic tests for external services. context: fork agent: test-generator hooks: PostToolUse: - matcher: "Write|Edit" command: "$CLAUDE_PROJECT_DIR/.claude/hooks/skill/test-runner.sh" Stop: - command: "$CLAUDE_PROJECT_DIR/.claude/hooks/skill/coverage-check.sh"
VCR.py HTTP Recording
Record and replay HTTP interactions for Python tests.
When to Use
- External API testing
- Deterministic HTTP tests
- Avoiding live API calls in CI
- LLM API response recording
Basic Setup
# 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"],
}
Basic Usage
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
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
@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 |
|---|---|
once | Record if missing, then replay |
new_episodes | Record new, replay existing |
none | Never record (CI) |
all | Always record (refresh) |
Filtering Sensitive Data
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,
}
LLM API Testing
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],
}
Cassette File Example
# 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
Key Decisions
| Decision | Recommendation |
|---|---|
| Record mode | once for dev, none for CI |
| Cassette format | YAML (readable) |
| Sensitive data | Always filter headers/body |
| Custom matchers | Use for LLM APIs |
Common Mistakes
- Committing cassettes with real API keys
- Using
allmode in CI (makes live calls) - Not filtering sensitive data
- Missing cassettes in git
Related Skills
msw-mocking- Frontend equivalentintegration-testing- API testing patternsllm-testing- LLM-specific patterns
Capability Details
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
cassette-replay
Keywords: replay, cassette, playback, mock replay Solves:
- Replay recorded HTTP interactions
- Run tests without network access
- Ensure consistent test results
async-support
Keywords: async, aiohttp, httpx async, async cassette Solves:
- Record async HTTP clients
- Handle aiohttp and httpx async
- Test async API integrations
sensitive-data-filtering
Keywords: filter, scrub, redact, sensitive data, before_record Solves:
- Scrub API keys from cassettes
- Redact sensitive data
- Implement before_record hooks
custom-matchers
Keywords: matcher, match on, request matching, custom match Solves:
- Configure request matching rules
- Ignore dynamic request parts
- Match by method/host/path
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
Repository

yonatangross
Author
yonatangross/skillforge-claude-plugin/.claude/skills/vcr-http-recording
5
Stars
1
Forks
Updated5d ago
Added1w ago