🚀 Big News: Socket Acquires Coana to Bring Reachability Analysis to Every Appsec Team.Learn more
Socket
DemoInstallSign in
Socket

pytest-recording

Package Overview
Dependencies
Maintainers
1
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

pytest-recording

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

0.13.4
PyPI
Maintainers
1

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:

.. code:: bash

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

.. code:: python

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:

.. code:: bash

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 <https://vcrpy.readthedocs.io/en/latest/usage.html#record-modes>`_

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.

.. code:: python

    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:

.. code:: python

    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"]}``

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

.. code:: python

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

.. code:: python

    import pytest

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

Or via command-line option:

.. code:: bash

    $ 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:

.. code:: python

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:

.. code:: python

import pytest
import requests

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

Run pytest:

.. code:: bash

$ 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:

.. code:: python

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:

.. code:: bash

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

Or via vcr_config fixture:

.. code:: python

import pytest

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

Additional resources

Looking for more examples? Check out this article <https://code.kiwi.com/pytest-cassettes-forget-about-mocks-or-live-requests-a9336e1caee6>_ about pytest-recording.

Contributing

To run the tests:

.. code:: bash

$ tox -p all

For more information, take a look at our contributing guide <https://github.com/kiwicom/pytest-recording/blob/master/CONTRIBUTING.rst>_

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:

.. code-block:: console

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.9, 3.10, 3.11, 3.12, and 3.13
  • PyPy 7.3 (3.10)

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.

.. |codecov| image:: https://codecov.io/gh/kiwicom/pytest-recording/branch/master/graph/badge.svg :target: https://codecov.io/gh/kiwicom/pytest-recording .. |Build| image:: https://github.com/kiwicom/pytest-recording/actions/workflows/build.yml/badge.svg :target: https://github.com/kiwicom/pytest-recording/actions?query=workflow%3Abuild .. |Version| image:: https://img.shields.io/pypi/v/pytest-recording.svg :target: https://pypi.org/project/pytest-recording/ .. |Python versions| image:: https://img.shields.io/pypi/pyversions/pytest-recording.svg :target: https://pypi.org/project/pytest-recording/ .. |License| image:: https://img.shields.io/pypi/l/pytest-recording.svg :target: https://opensource.org/licenses/MIT

.. _MIT license: https://opensource.org/licenses/MIT

Keywords

mock

FAQs

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts