Automatically mock your HTTP interactions to simplify and speed up testing
Cassette serialization and deserialization support for YAML and JSON formats with extensible serializer registration. VCR.py provides flexible serialization options for storing recorded HTTP interactions.
Default serializer using YAML format for human-readable cassette files.
# From vcr.serializers.yamlserializer module
def deserialize(cassette_string: str) -> dict:
"""
Deserialize YAML cassette data into Python dictionary.
Args:
cassette_string: YAML-formatted cassette data as string
Returns:
dict: Parsed cassette data with interactions and metadata
Raises:
yaml.YAMLError: If YAML parsing fails
"""
def serialize(cassette_dict: dict) -> str:
"""
Serialize cassette data dictionary to YAML format.
Args:
cassette_dict: Cassette data as Python dictionary
Returns:
str: YAML-formatted cassette data
Raises:
yaml.YAMLError: If YAML serialization fails
"""Alternative serializer using JSON format for compact cassette files.
# From vcr.serializers.jsonserializer module
def deserialize(cassette_string: str) -> dict:
"""
Deserialize JSON cassette data into Python dictionary.
Args:
cassette_string: JSON-formatted cassette data as string
Returns:
dict: Parsed cassette data with interactions and metadata
Raises:
json.JSONDecodeError: If JSON parsing fails
"""
def serialize(cassette_dict: dict) -> str:
"""
Serialize cassette data dictionary to JSON format.
Args:
cassette_dict: Cassette data as Python dictionary
Returns:
str: JSON-formatted cassette data
Raises:
TypeError: If data contains non-JSON-serializable objects
"""import vcr
# Default YAML serialization (human-readable)
yaml_vcr = vcr.VCR(serializer='yaml')
@yaml_vcr.use_cassette('interactions.yaml')
def test_with_yaml():
response = requests.get('https://api.example.com/data')
# Creates interactions.yaml file
# JSON serialization (more compact)
json_vcr = vcr.VCR(serializer='json')
@json_vcr.use_cassette('interactions.json')
def test_with_json():
response = requests.get('https://api.example.com/data')
# Creates interactions.json filebase_vcr = vcr.VCR(serializer='yaml') # Default to YAML
# Override for specific test
@base_vcr.use_cassette('special.json', serializer='json')
def test_with_json_override():
response = requests.get('https://api.example.com/data')
# Uses JSON despite base VCR using YAMLimport json
import gzip
import vcr
class CompressedJSONSerializer:
"""Custom serializer that compresses JSON data."""
@staticmethod
def serialize(cassette_dict):
"""Serialize and compress cassette data."""
json_data = json.dumps(cassette_dict, indent=2)
compressed_data = gzip.compress(json_data.encode('utf-8'))
return compressed_data
@staticmethod
def deserialize(cassette_string):
"""Decompress and deserialize cassette data."""
if isinstance(cassette_string, bytes):
decompressed_data = gzip.decompress(cassette_string)
json_data = decompressed_data.decode('utf-8')
else:
json_data = cassette_string
return json.loads(json_data)
# Register custom serializer
my_vcr = vcr.VCR()
my_vcr.register_serializer('compressed_json', CompressedJSONSerializer)
@my_vcr.use_cassette('compressed.cjson', serializer='compressed_json')
def test_with_compressed_json():
response = requests.get('https://api.example.com/data')import json
class PrettyJSONSerializer:
"""JSON serializer with human-readable formatting."""
@staticmethod
def serialize(cassette_dict):
return json.dumps(
cassette_dict,
indent=2,
sort_keys=True,
separators=(',', ': ')
)
@staticmethod
def deserialize(cassette_string):
return json.loads(cassette_string)
my_vcr = vcr.VCR()
my_vcr.register_serializer('pretty_json', PrettyJSONSerializer)import json
import base64
class BinarySafeJSONSerializer:
"""JSON serializer that handles binary data in responses."""
@staticmethod
def serialize(cassette_dict):
"""Serialize with binary data encoded as base64."""
# Deep copy to avoid modifying original
import copy
safe_dict = copy.deepcopy(cassette_dict)
# Encode binary response bodies
for interaction in safe_dict.get('interactions', []):
response = interaction.get('response', {})
body = response.get('body', {})
if isinstance(body.get('string'), bytes):
# Encode binary data as base64
body['string'] = base64.b64encode(body['string']).decode('ascii')
body['encoding'] = 'base64'
return json.dumps(safe_dict, indent=2)
@staticmethod
def deserialize(cassette_string):
"""Deserialize with base64 decoding of binary data."""
cassette_dict = json.loads(cassette_string)
# Decode binary response bodies
for interaction in cassette_dict.get('interactions', []):
response = interaction.get('response', {})
body = response.get('body', {})
if body.get('encoding') == 'base64':
# Decode base64 data back to bytes
body['string'] = base64.b64decode(body['string'].encode('ascii'))
del body['encoding']
return cassette_dict
my_vcr = vcr.VCR()
my_vcr.register_serializer('binary_safe_json', BinarySafeJSONSerializer)interactions:
- request:
body: null
headers:
Accept:
- '*/*'
User-Agent:
- python-requests/2.28.1
method: GET
uri: https://api.example.com/data
response:
body:
string: '{"message": "Hello, World!"}'
headers:
Content-Type:
- application/json
Content-Length:
- '26'
status:
code: 200
message: OK
version: 1{
"interactions": [
{
"request": {
"body": null,
"headers": {
"Accept": ["*/*"],
"User-Agent": ["python-requests/2.28.1"]
},
"method": "GET",
"uri": "https://api.example.com/data"
},
"response": {
"body": {
"string": "{\"message\": \"Hello, World!\"}"
},
"headers": {
"Content-Type": ["application/json"],
"Content-Length": ["26"]
},
"status": {
"code": 200,
"message": "OK"
}
}
}
],
"version": 1
}import os
import vcr
def get_serializer():
"""Choose serializer based on environment."""
serializer = os.getenv('VCR_SERIALIZER', 'yaml')
return serializer
my_vcr = vcr.VCR(serializer=get_serializer())
# Usage: VCR_SERIALIZER=json python test.pyimport json
import os
class SmartJSONSerializer:
"""JSON serializer with environment-based features."""
@staticmethod
def serialize(cassette_dict):
# Compact format in production, pretty format in development
if os.getenv('ENV') == 'production':
return json.dumps(cassette_dict, separators=(',', ':'))
else:
return json.dumps(cassette_dict, indent=2, sort_keys=True)
@staticmethod
def deserialize(cassette_string):
return json.loads(cassette_string)import json
class VersionedJSONSerializer:
"""JSON serializer that includes format version information."""
CURRENT_VERSION = 2
@staticmethod
def serialize(cassette_dict):
# Add version information
versioned_dict = {
'format_version': VersionedJSONSerializer.CURRENT_VERSION,
'data': cassette_dict
}
return json.dumps(versioned_dict, indent=2)
@staticmethod
def deserialize(cassette_string):
versioned_dict = json.loads(cassette_string)
# Handle different format versions
format_version = versioned_dict.get('format_version', 1)
if format_version == 1:
# Legacy format - data is at root level
return versioned_dict
elif format_version == 2:
# Current format - data is nested
return versioned_dict['data']
else:
raise ValueError(f"Unsupported cassette format version: {format_version}")import json
import base64
from cryptography.fernet import Fernet
class EncryptedJSONSerializer:
"""JSON serializer with encryption for sensitive data."""
def __init__(self, encryption_key=None):
if encryption_key is None:
encryption_key = Fernet.generate_key()
self.cipher = Fernet(encryption_key)
def serialize(self, cassette_dict):
"""Encrypt and serialize cassette data."""
json_data = json.dumps(cassette_dict)
encrypted_data = self.cipher.encrypt(json_data.encode())
return base64.b64encode(encrypted_data).decode()
def deserialize(self, cassette_string):
"""Decrypt and deserialize cassette data."""
encrypted_data = base64.b64decode(cassette_string.encode())
decrypted_data = self.cipher.decrypt(encrypted_data)
return json.loads(decrypted_data.decode())
# Usage with encryption key from environment
import os
key = os.getenv('VCR_ENCRYPTION_KEY')
if key:
encrypted_serializer = EncryptedJSONSerializer(key.encode())
my_vcr = vcr.VCR()
my_vcr.register_serializer('encrypted', encrypted_serializer)import json
import ijson # For streaming JSON parsing
class StreamingJSONSerializer:
"""Serializer optimized for large cassette files."""
@staticmethod
def serialize(cassette_dict):
"""Standard JSON serialization."""
return json.dumps(cassette_dict)
@staticmethod
def deserialize(cassette_string):
"""Stream parsing for large JSON files."""
if len(cassette_string) > 10 * 1024 * 1024: # > 10MB
# Use streaming parser for large files
import io
stream = io.StringIO(cassette_string)
return ijson.parse(stream)
else:
# Use standard parser for smaller files
return json.loads(cassette_string)Install with Tessl CLI
npx tessl i tessl/pypi-vcrpy