replayx

Record and replay HTTP and HTTPS interactions for httpx. Run your tests fast and offline.

PyPI Python versions MIT License
$ pip install replayx

The first run records. Later runs replay.

replayx saves real HTTP responses to a cassette file on the first test run. Every later run reads from the cassette. No network calls. No flaky tests. No slow CI.

import httpx
from replayx import use_cassette

with use_cassette("cassettes/github.json"):
    resp = httpx.get("https://api.github.com/users/octocat")
    assert resp.json()["login"] == "octocat"

What you get

Async first

Full support for httpx.AsyncClient and the sync client. One library for both.

Zero extra deps

JSON cassettes need nothing beyond httpx. Add YAML with one extra.

Secret redaction

Strip headers, query params, and bodies at record time. Commit cassettes with no leaks.

No magic option

Use the patching context manager, or build a transport yourself. Your call.

pytest plugin

Auto-named cassettes per test. Re-record a run with one flag.

Typed

Ships py.typed. Strict mypy passes. Clear, readable cassettes.

Three ways to use replayx

Patch httpx automatically

with use_cassette("cassettes/data.json"):
    async with httpx.AsyncClient() as client:
        resp = await client.get("https://api.example.com/data")

Build a transport yourself

cassette = Cassette.load("cassettes/data.json", record_mode="once")
with httpx.Client(transport=cassette.sync_transport()) as client:
    resp = client.get("https://api.example.com/data")
cassette.save()

Use the pytest fixture

def test_octocat(replayx_cassette):
    with replayx_cassette():
        resp = httpx.get("https://api.github.com/users/octocat")
        assert resp.status_code == 200

Record modes

ModeWhat happens
onceReplay an existing cassette. Record when none exists. A new request raises an error.
new_episodesReplay matches and append new interactions.
noneReplay only. No network. No writes. Good for CI.
allReach the real backend and overwrite the cassette. Use to re-record.