CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-vcrpy

Automatically mock your HTTP interactions to simplify and speed up testing

Overview
Eval results
Files

serialization.mddocs/

Serialization

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.

Capabilities

YAML Serializer

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
    """

JSON Serializer

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
    """

Usage Examples

Choosing Serialization Format

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 file

Per-Test Serializer Override

base_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 YAML

Custom Serializer Implementation

import 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')

Pretty-Printed JSON Serializer

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)

Binary-Safe Serializer

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)

Cassette File Format

YAML Cassette Structure

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

JSON Cassette Structure

{
  "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
}

Advanced Serialization Patterns

Environment-Based Serializer Selection

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.py

Conditional Serializer Features

import 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)

Versioned Serializer

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}")

Encrypted Serializer

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)

Streaming Serializer for Large Cassettes

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

docs

core-configuration.md

data-filtering.md

error-handling.md

index.md

record-modes.md

request-matching.md

request-response.md

serialization.md

test-integration.md

tile.json