VCR.py

repository·master·Indexed 25 days ago

https://github.com/kevin1024/vcrpy

A Python library that records and replays HTTP interactions to make tests faster, deterministic, and capable of running offline. It supports Python 3.9+ and PyPy, and is compatible with HTTP libraries including requests, httpx, aiohttp, boto3, and urllib3. Features include custom serializers, request matchers, cassette persisters, and sensitive data filtering.

Tokens
6.5K
Snippets
20
Records
32
Agent score
83%

What's inside vcrpy

  1. Overview of VCR.py

    master

    VCR.py is a Python library designed to simplify and speed up tests that make HTTP requests. It works by recording HTTP interactions (requests and responses) the first time a test is run and serializing them into a flat file called a 'cassette' (defaulting to YAML format).

    On subsequent test runs, VCR.py intercepts matching HTTP requests and returns the recorded responses from the cassette instead of making actual network traffic. This provides:

    • Offline capability: Run tests without an internet connection.
    • Determinism: Tests are completely deterministic because they use recorded data.
    • Speed: Test execution is significantly faster as it avoids network latency.

    To update cassettes if an API changes, simply delete the existing cassette files and re-run your tests to record the new interactions.

  2. Override VCR options per cassette

    master

    You can override global VCR settings on a per-cassette basis by passing arguments directly to vcr.use_cassette(). Per-cassette overrides take precedence over the global configuration set in a vcr.VCR instance.

    import vcr
    
    with vcr.use_cassette('test.yml', serializer='json', record_mode='once'):
        # your http code here
  3. Ignore specific requests

    master

    You can prevent VCR from interacting with certain requests using these methods:

    • ignore_localhost=True: Ignores requests to localhost, 127.0.0.1, or 0.0.0.0.
    • ignore_hosts: A list of hostnames to ignore.
    • before_record_request / before_record_response: Return None in these callbacks to ignore specific interactions.
  4. Handle custom Python objects in YAML cassettes

    master

    VCR.py uses a safe YAML loader that blocks !!python/object tags for security. To record/replay cassettes containing custom Python objects, register a custom serializer using yamlserializer.with_custom_tags. This allows you to define how specific tags are constructed during loading.

    import vcr
    from vcr.serializers import yamlserializer
    
    def construct_my_header(loader, node):
        return MyHeader(loader.construct_sequence(node)[0])
    
    my_vcr = vcr.VCR()
    my_vcr.register_serializer("yaml", yamlserializer.with_custom_tags({
        "tag:yaml.org,2002:python/object/new:myapp.MyHeader": construct_my_header,
    }))
  5. Use VCR.py with context managers or decorators

    master

    You can use VCR.py to record and replay HTTP interactions using either a context manager or a decorator.

    Context Manager: Wrap your HTTP calls in vcr.use_cassette('path/to/cassette.yaml'). The first run records the request to the specified file; subsequent runs replay it.

    Decorator: Apply @vcr.use_cassette('path/to/cassette.yaml') to a test function. If you omit the path in the decorator, VCR.py will automatically name the cassette after the test function and place it in the same directory as the test file.

    import vcr
    import urllib.request
    
    # Using a context manager
    with vcr.use_cassette('fixtures/vcr_cassettes/synopsis.yaml'):
        response = urllib.request.urlopen('http://www.iana.org/domains/reserved').read()
        assert b'Example domains' in response
    
    # Using a decorator
    @vcr.use_cassette('fixtures/vcr_cassettes/synopsis.yaml')
    def test_iana():
        response = urllib.request.urlopen('http://www.iana.org/domains/reserved').read()
        assert b'Example domains' in response
    
    # Using a decorator with automatic naming
    @vcr.use_cassette()
    def test_iana_auto():
        response = urllib.request.urlopen('http://www.iana.org/domains/reserved').read()
        assert b'Example domains' in response
  6. Integrate VCR.py with Pytest

    master

    For Pytest users, use the pytest-recording plugin. It provides fixtures for recording cassettes and supports blocking network access.

    Note: pytest-vcr is an older, unmaintained plugin and is not recommended for new projects.

  7. Use automatic cassette naming

    master

    You can omit the path argument in use_cassette to automatically generate a filename based on the decorated function's name.

    • If cassette_library_dir is NOT set: The cassette is saved in the same directory as the test function.
    • If cassette_library_dir IS set: The cassette is saved in that directory.

    You can customize the naming behavior using path_transformer and func_path_generator. To append a specific extension to all automatically named cassettes, use VCR.ensure_suffix.

    from vcr import VCR
    
    # Add a specific extension to all automatic cassette names
    my_vcr = VCR(path_transformer=VCR.ensure_suffix('.yaml'))
    
    @my_vcr.use_cassette
    def my_test_function():
        ...
  8. Use custom request and response callbacks

    master

    Use before_record_request or before_record_response to manipulate requests/responses before they are saved to a cassette.

    • To mutate: Modify the request/response object and return it.
    • To ignore: Return None to prevent the interaction from being recorded.
    # Example: Ignore requests to '/login'
    def before_record_cb(request):
        if request.path == '/login':
            return None
        return request
    
    my_vcr = vcr.VCR(
        before_record_request=before_record_cb,
    )
    
    # Example: Scrub sensitive data from response body
    def scrub_string(string, replacement=''):
        def before_record_response(response):
            response['body']['string'] = response['body']['string'].replace(string, replacement)
            return response
        return before_record_response
    
    my_vcr = vcr.VCR(
        before_record_response=scrub_string(settings.USERNAME, 'username'),
    )
  9. Configure VCR global settings via the VCR class

    master

    To customize VCR's default behavior globally, instantiate a vcr.VCR object with your desired options. This object can then be used to manage cassettes via its .use_cassette() method.

    Common configuration options include:

    • serializer: The format used to save cassettes (e.g., 'json').
    • cassette_library_dir: The directory where cassettes are stored.
    • record_mode: Controls how new interactions are recorded (e.g., 'once').
    • match_on: A list of attributes used to match requests.
    import vcr
    
    my_vcr = vcr.VCR(
        serializer='json',
        cassette_library_dir='fixtures/cassettes',
        record_mode='once',
        match_on=['uri', 'method'],
    )
    
    with my_vcr.use_cassette('test.json'):
        # your http code here
  10. Filter sensitive data from requests

    master

    To prevent sensitive information from being recorded in cassettes, use the following configuration options:

    • filter_headers: List of header names to filter.
    • filter_query_parameters: List of query parameter names to filter.
    • filter_post_data_parameters: List of POST data parameter names to filter.

    Advanced Filtering: Instead of a simple list of strings, you can pass a list of (key, value) tuples where value can be:

    • A new value to replace the original.
    • None to remove the key/value pair.
    • A callable that returns a new value or None.