Replay in pytest
The Python flow mirrors Replay in Jest — the same case file, the same control API, the same findInCase + mockOutbound pattern. If you've read the Jest guide, skim this for the Python-specific bits.
1. Install the SDK
Not yet on PyPI
softprobe is not yet published to PyPI. Until it is, install pytest from PyPI and consume softprobe from source in the softprobe monorepo (softprobe-python/) — see that package's README.md for the editable-install recipe.
# Planned — not yet published.
pip install softprobe pytest2. The minimum working test
# tests/test_checkout_replay.py
import os
from pathlib import Path
import pytest
import urllib.request
from softprobe import Softprobe
APP_URL = os.environ.get("APP_URL", "http://127.0.0.1:8082")
softprobe = Softprobe() # reads SOFTPROBE_RUNTIME_URL; defaults to https://runtime.softprobe.dev
@pytest.fixture(scope="module")
def session():
s = softprobe.start_session(mode="replay")
s.load_case_from_file(Path(__file__).parent.parent / "cases" / "checkout-happy-path.case.json")
hit = s.find_in_case(
direction="outbound",
method="POST",
host_suffix="stripe.com",
path_prefix="/v1/payment_intents",
)
s.mock_outbound(
direction="outbound",
method="POST",
host_suffix="stripe.com",
path_prefix="/v1/payment_intents",
response=hit.response,
)
yield s
s.close()
def test_charges_the_captured_card(session):
req = urllib.request.Request(
f"{APP_URL}/checkout",
method="POST",
data=b'{"amount": 1000, "currency": "usd"}',
headers={
"content-type": "application/json",
"x-softprobe-session-id": session.id,
},
)
with urllib.request.urlopen(req) as res:
assert res.status == 200
body = res.read().decode()
assert '"status": "paid"' in body3. Run it
pytest tests/test_checkout_replay.py -vExpected:
tests/test_checkout_replay.py::test_charges_the_captured_card PASSED [100%]API parity with Jest
| JavaScript | Python |
|---|---|
new Softprobe({ baseUrl }) | Softprobe(base_url=...) |
softprobe.startSession({ mode: 'replay' }) | softprobe.start_session(mode="replay") |
session.loadCaseFromFile(path) | session.load_case_from_file(path) |
session.findInCase({ direction, method, hostSuffix }) | session.find_in_case(direction=..., method=..., host_suffix=...) |
session.mockOutbound({ ..., response }) | session.mock_outbound(..., response=...) |
session.clearRules() | session.clear_rules() |
session.setPolicy({ externalHttp: 'strict' }) | session.set_policy(external_http="strict") |
session.close() | session.close() |
Python uses snake_case; semantics and HTTP wire shape are identical.
Mutating a captured response
import json
hit = session.find_in_case(direction="outbound", host_suffix="stripe.com")
body = json.loads(hit.response.body)
body["source"]["card"]["number"] = os.environ.get("TEST_CARD", "4111111111111111")
session.mock_outbound(
host_suffix="stripe.com",
response={
"status": hit.response.status,
"headers": hit.response.headers,
"body": json.dumps(body),
},
)Parametrizing over captured scenarios
If you have a folder of case files, you can drive each one as a pytest parameter:
import glob
from pathlib import Path
CASE_DIR = Path(__file__).parent.parent / "cases"
CASE_FILES = sorted(glob.glob(str(CASE_DIR / "checkout-*.case.json")))
@pytest.mark.parametrize("case_path", CASE_FILES)
def test_replay_case(case_path):
s = softprobe.start_session(mode="replay")
try:
s.load_case_from_file(case_path)
hit = s.find_in_case(direction="outbound", host_suffix="stripe.com")
s.mock_outbound(host_suffix="stripe.com", response=hit.response)
req = urllib.request.Request(
f"{APP_URL}/checkout",
method="POST",
data=b'{"amount": 1000}',
headers={"x-softprobe-session-id": s.id},
)
with urllib.request.urlopen(req) as res:
assert res.status == 200
finally:
s.close()For 100s of cases, prefer softprobe suite run over pytest parametrization — it's faster and designed for the scale case.
Using a pytest plugin for conftest.py
The SDK ships a small pytest plugin that handles fixture wiring. In conftest.py:
from softprobe.pytest_plugin import softprobe_session # re-exports a fixtureThen in your test:
def test_charges_the_captured_card(softprobe_session):
softprobe_session.load_case_from_file("cases/checkout-happy-path.case.json")
hit = softprobe_session.find_in_case(host_suffix="stripe.com")
softprobe_session.mock_outbound(host_suffix="stripe.com", response=hit.response)
# ...The plugin handles create / close + env var inference so you don't repeat boilerplate per file.
Running in parallel
pytest-xdist works with Softprobe out of the box — each worker creates its own session. Set -n auto:
pytest -n autoNext
- Run a suite at scale — for hundreds or thousands of cases.
- Write a hook — for PII masking or custom assertions shared with CLI-driven suites.
- Python SDK reference — complete API.