nimax
Record and replay niquests HTTP and WebSocket interactions in pytest.
nimax is a VCR-style cassette library built natively for niquests — supporting lazy responses, multiplexed connections, AsyncSession, and WebSockets. It is to niquests what betamax is to requests.
Installation
pip install nimax
Quick start
Automatic fixture
nimax registers a nimax_session pytest fixture automatically. Use it instead of niquests.Session() in your tests:
def test_my_api(nimax_session):
resp = nimax_session.get("https://api.example.com/users")
assert resp.status_code == 200
On the first run nimax records the real HTTP response to a cassette file under cassettes/<test_module>/<test_name>.json. Subsequent runs replay from the cassette — no network required.
Async sessions
import pytest
import niquests
async def test_async(nimax_session):
async with niquests.AsyncSession() as session:
with NimaxRecorder(session).use_cassette("my_cassette.json"):
resp = await session.get("https://api.example.com/data")
assert resp.status_code == 200
Programmatic API
import niquests
from nimax import NimaxRecorder, RecordMode
def test_programmatic(tmp_path):
session = niquests.Session()
cassette_path = tmp_path / "my_cassette.json"
with NimaxRecorder(session).use_cassette(cassette_path, record_mode=RecordMode.ONCE):
resp = session.get("https://api.example.com/users")
assert resp.status_code == 200
Record modes
| Mode | Behaviour |
|---|---|
once |
Record on first run, replay on subsequent runs (default) |
none |
Never record — raise an error if no matching interaction exists |
new_episodes |
Replay existing interactions; record any unmatched requests |
all |
Always record, overwriting the cassette each run |
WebSocket support
WebSocket connections opened through a cassette-backed session are recorded and replayed automatically, alongside HTTP interactions, in the same cassette file:
def test_ws_echo(nimax_session):
resp = nimax_session.get("wss://echo.example.com")
resp.extension.send_payload('{"id": "1", "op": "ping"}')
reply = resp.extension.next_payload()
On replay, a recv frame only releases once the real send_payload() call it depends on has actually happened — matching how a real socket can't deliver a response before its triggering request went out. By default this is tracked by position (send count) in the recorded log, which is correct as long as your live send order doesn't diverge from the recorded order.
Correlating responses by id
If your protocol embeds a correlation id in its messages (e.g. JSON-RPC-style {"id": ..., ...}) and you dispatch responses from a background reader task — so concurrent, in-flight requests can legitimately resolve out of order — pass ws_id_extractor to correlate replay by that id instead of by position:
with NimaxRecorder(session).use_cassette("my_cassette.json", ws_id_extractor="id"):
...
ws_id_extractor accepts:
- A dotted JSON path string, e.g.
"id"or"params.id"for a nested field. - A callable
(payload: str | bytes) -> Any | Nonefor anything else (non-JSON protocols, custom shapes).
A message whose id doesn't resolve (e.g. valid JSON with no id field) falls back to the position-based gate. A payload the extractor can't parse at all (e.g. malformed JSON when JSON was expected) raises — that means the extractor doesn't match the actual protocol, which is worth surfacing rather than silently ignoring.
With the automatic nimax_session/nimax_async_session fixtures, set a dotted-path string project-wide via [tool.nimax] in pyproject.toml:
[tool.nimax]
ws_id_extractor = "id"
A callable can't live in static config, so to use one with the automatic fixtures, override the nimax_ws_id_extractor fixture in your own conftest.py:
import pytest
@pytest.fixture
def nimax_ws_id_extractor():
return my_custom_extractor
Placeholders
Scrub sensitive values (tokens, API keys) from cassettes before they are written:
from nimax import Placeholder
recorder = NimaxRecorder(
session,
placeholders=[
Placeholder(placeholder="<AUTH_TOKEN>", replace="Bearer secret123"),
],
)
Custom matchers and serializers
from nimax import BaseMatcher, NimaxRecorder
class BodyMatcher(BaseMatcher):
name = "body"
def match(self, recorded: dict, live: object) -> bool:
return recorded.get("body") == live.body # type: ignore[union-attr]
NimaxRecorder.register_matcher(BodyMatcher)
YAML cassettes are supported out of the box — use a .yaml extension for the cassette path.
Requirements
- Python ≥ 3.11
- niquests ≥ 3
- pytest ≥ 8
- PyYAML ≥ 6
License
MIT
Metadata
Release files for nimax 1.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| nimax-1.1.1.tar.gz | 30.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| nimax-1.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 51.5 kB
Release files / nimax-1.1.1.tar.gz
| Download URL | nimax-1.1.1.tar.gz |
|---|---|
| Size | 30.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
dfea909da0cb3b3c068f25a2d3d9f10a15850dd19150bd4027c5b00aaf24ef23
|
|
BLAKE2b-256 checksum How to use checksums |
7a8f71abd473081fa4a00009018a6bc0dd6e1ad94b7cf8fffa6acd325f8f400d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 16, 2026.
Transparency logRelease files / nimax-1.1.1-py3-none-any.whl
| Download URL | nimax-1.1.1-py3-none-any.whl |
|---|---|
| Size | 20.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d36f814edb46d81922fe1640e0293e0faee7a7f3ef628b4fb675adb54e1c7d5b
|
|
BLAKE2b-256 checksum How to use checksums |
1a4120c497a146e71d2b5d9dba64474a5a7c6fb502bfb44cc9cfa0bbe49062c3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 16, 2026.
Transparency log