Skip to main content

pytest-recording

codecov Build Version Python versions License

A pytest plugin powered by VCR.py to record and replay HTTP traffic.

Features

  • Straightforward pytest.mark.vcr, that reflects VCR.use_cassettes API;

  • Combining multiple VCR cassettes;

  • Network access blocking;

  • The rewrite recording mode that rewrites cassettes from scratch.

Installation

This project can be installed via pip:

pip install pytest-recording

⚠️ Incompatibility Warning

If you have pytest-vcr installed, please uninstall it before using pytest-recording, as the two plugins are incompatible.

Usage

import pytest
import requests

# cassettes/{module_name}/test_single.yaml will be used
@pytest.mark.vcr
def test_single():
    assert requests.get("http://httpbin.org/get").text == '{"get": true}'

# cassettes/{module_name}/example.yaml will be used
@pytest.mark.default_cassette("example.yaml")
@pytest.mark.vcr
def test_default():
    assert requests.get("http://httpbin.org/get").text == '{"get": true}'

# these cassettes will be used in addition to the default one
@pytest.mark.vcr("/path/to/ip.yaml", "/path/to/get.yaml")
def test_multiple():
    assert requests.get("http://httpbin.org/get").text == '{"get": true}'
    assert requests.get("http://httpbin.org/ip").text == '{"ip": true}'

# Make assertions based on the cassette calls/responses:
@pytest.mark.vcr
def test_call_count(vcr):
    assert requests.get("http://httpbin.org/get").text == '{"get": true}'
    assert requests.get("http://httpbin.org/ip").text == '{"ip": true}'
    # See https://vcrpy.readthedocs.io/en/latest/advanced.html for more info
    # about the Cassette object:
    assert vcr.play_count == 2

Run your tests:

pytest --record-mode=once test_network.py

Default recording mode

pytest-recording uses the none VCR recording mode by default to prevent unintentional network requests. To allow them you need to pass a different recording mode (e.g. once) via the --record-mode CLI option to your test command. See more information about available recording modes in the official VCR documentation

Configuration

You can provide the recording configuration with the vcr_config fixture, which could be any scope - session, package, module, or function. It should return a dictionary that will be passed directly to VCR.use_cassettes under the hood.

import pytest

@pytest.fixture(scope="module")
def vcr_config():
    return {"filter_headers": ["authorization"]}

For more granular control you need to pass these keyword arguments to individual pytest.mark.vcr marks, and in this case all arguments will be merged into a single dictionary with the following priority (low -> high):

  • vcr_config fixture

  • all marks from the most broad scope (“session”) to the most narrow one (“function”)

Example:

import pytest

pytestmark = [pytest.mark.vcr(ignore_localhost=True)]

@pytest.fixture(scope="module")
def vcr_config():
    return {"filter_headers": ["authorization"]}

@pytest.mark.vcr(filter_headers=[])
def test_one():
    ...

@pytest.mark.vcr(filter_query_parameters=["api_key"])
def test_two():
    ...

Resulting VCR configs for each test:

  • test_one - {"ignore_localhost": True, "filter_headers": []}

  • test_two - {"ignore_localhost": True, "filter_headers": ["authorization"], "filter_query_parameters": ["api_key"]}

The vcr_config fixture forwards any keyword argument to vcrpy, so ignore_hosts and other VCR.py options work directly:

import pytest

@pytest.fixture(scope="module")
def vcr_config():
    return {
        "filter_headers": ["authorization"],
        "ignore_hosts": ["169.254.169.254", "metadata.google.internal"],
    }

You can get access to the used VCR instance via pytest_recording_configure hook. It might be useful for registering custom matchers, persisters, etc.:

# conftest.py

def jurassic_matcher(r1, r2):
    assert r1.uri == r2.uri and "JURASSIC PARK" in r1.body, \
        "required string (JURASSIC PARK) not found in request body"

def pytest_recording_configure(config, vcr):
    vcr.register_matcher("jurassic", jurassic_matcher)

You can disable the VCR.py integration entirely by passing the --disable-recording CLI option.

Rewrite record mode

It is possible to rewrite a cassette from scratch and not extend it with new entries as it works now with the all record mode from VCR.py.

However, it will rewrite only the default cassette and won’t touch extra cassettes.

import pytest

@pytest.fixture(scope="module")
def vcr_config():
    return {"record_mode": "rewrite"}

Or via command-line option:

$ pytest --record-mode=rewrite tests/

Blocking network access

To have more confidence that your tests will not go over the wire, you can block it with pytest.mark.block_network mark:

import pytest
import requests

@pytest.mark.block_network
def test_multiple():
    assert requests.get("http://httpbin.org/get").text == '{"get": true}'

...
# in case of access
RuntimeError: Network is disabled

Besides marks, the network access could be blocked globally with --block-network command-line option.

However, if VCR.py recording is enabled, the network is not blocked for tests with pytest.mark.vcr.

Example:

import pytest
import requests

@pytest.mark.vcr
def test_multiple():
    assert requests.get("http://httpbin.org/get").text == '{"get": true}'

Run pytest:

$ pytest --record-mode=once --block-network tests/

The network blocking feature supports socket-based transports and pycurl.

It is possible to allow access to specified hosts during network blocking:

import pytest
import requests

@pytest.mark.block_network(allowed_hosts=["httpbin.*"])
def test_access():
    assert requests.get("http://httpbin.org/get").text == '{"get": true}'
    with pytest.raises(RuntimeError, match=r"^Network is disabled$"):
        requests.get("http://example.com")

Or via command-line option:

$ pytest --record-mode=once --block-network --allowed-hosts=httpbin.*,localhost tests/

Or via vcr_config fixture:

import pytest

@pytest.fixture(autouse=True)
def vcr_config():
    return {"allowed_hosts": ["httpbin.*"]}

Additional resources

Looking for more examples? Check out this article about pytest-recording.

Contributing

To run the tests:

$ tox -p all

For more information, take a look at our contributing guide

Test Isolation for Package Maintainers

When running pytest-based tests in a packaging environment, globally installed plugins can break test suites by injecting unexpected hooks or fixtures (e.g. pytest-pretty) that your code isn’t designed for.

To guarantee a clean, reproducible test run:

export PYTEST_DISABLE_PLUGIN_AUTOLOAD=1
export PYTEST_PLUGINS=pytest_httpbin.plugin,pytest_mock,pytest_recording.plugin
  • PYTEST_DISABLE_PLUGIN_AUTOLOAD Disables loading of any plugins via setuptools entry-points; only those you explicitly list will be activated.

  • PYTEST_PLUGINS Comma-separated list of plugin modules pytest should load (the core plugin manager still discovers builtin plugins and conftest.py).

Include these exports in your package’s build or CI script so that system-wide pytest plugins (e.g. linting, formatting, or unrelated test helpers) cannot interfere with your tests.

Python support

Pytest-recording supports:

  • CPython 3.10, 3.11, 3.12, 3.13, 3.14 and 3.15

  • PyPy 7.3 (3.11)

License

The code in this project is licensed under MIT license. By contributing to pytest-recording, you agree that your contributions will be licensed under its MIT license.

Metadata

Release files for pytest-recording 0.14.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pytest-recording 0.14.0
File Size Uploaded
pytest_recording-0.14.0.tar.gz 27.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pytest-recording 0.14.0
File Interpreter ABI Platform
pytest_recording-0.14.0-py3-none-any.whl Python 3 none any Details

Total release size: 41.0 kB

Release files / pytest_recording-0.14.0.tar.gz

Download URL pytest_recording-0.14.0.tar.gz
Size 27.1 kB
Tags Source
SHA-256 checksum
How to use checksums
175f62a71da36c0a019dbee47f92b9fa4c72dea18e215da1488282e8d4d08b35
BLAKE2b-256 checksum
How to use checksums
5ff10c995888c28d4c76c7b0efdafaa8e9f8f2337730c1ea8dc33329bc04dd2a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / pytest_recording-0.14.0-py3-none-any.whl

Download URL pytest_recording-0.14.0-py3-none-any.whl
Size 13.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
419f1a9325827987043d01a33a26dcafa69c1744521e1ed1ffa7c7b5fabc865c
BLAKE2b-256 checksum
How to use checksums
62f17cb1ed94d6d37a585e28951a3f34af8bea09cd7c0cad562345b150489f60
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release history Release notifications | RSS feed

This release

0.14.0 This release

2 release files

0.13.3

2 release files

0.13.0

2 release files

0.12.2

2 release files

0.12.1

2 release files

0.11.0

2 release files

0.9.0

1 release file

0.8.1

2 release files

0.8.0

2 release files

0.7.0

2 release files

0.6.0

1 release file

0.5.0

2 release files

0.4.0

2 release files

0.3.6

2 release files

0.3.5

1 release file

0.3.4

1 release file

0.3.3

1 release file

0.3.2

1 release file

0.3.1

1 release file

0.3.0

1 release file

0.2.0

1 release file

0.1.0

1 release file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page