Keploy logo
Python logo

Keploy as a Python testing framework

Keploy tests Python applications by recording real HTTP traffic and every downstream call the request triggered, then replaying both as assertions. You add no fixtures, no unittest.mock patches, and no test containers — Keploy runs your existing app and derives the suite from observed behaviour.

Generate Python tests free
keploy record -c "python manage.py runserver"
18.4K+VS Code1.2M+300M+mocks created

What Keploy gives a Python team

Any CPython service, unchanged

Keploy wraps the command you already run. Django, FastAPI, Flask, and Litestar all record identically, and WSGI versus ASGI makes no difference because capture happens below the interpreter.

  • CPython 3.8 and above
  • runserver, uvicorn, or gunicorn
  • Sync and async views record the same way
  • Middleware stacks execute in full
The problem

Why Python integration tests slow teams down

The friction is rarely the assertions. It is everything around them — spinning up dependencies, keeping mocks honest, and repairing tests after every refactor.

Three ways to do it

Testing Python: by hand, with pytest + unittest.mock, or with Keploy

pytest and unittest.mock start from your model of a dependency. Keploy starts from the response the dependency actually sent, so a mock cannot quietly drift from the API it stands in for.

Select any row for the full comparison, with code.

Same coverage, three costs

What you write for Python vs what Keploy records

All three produce the same assertion. Only the third still passes after the next refactor without anyone editing it.

By hand2–4 hours
test_orders_manual.py
hand-written
# Hand-written: fakes, payloads, and assertions all by hand.
from app.api import create_app
from app.orders import OrderService
 
class FakePaymentClient:
def charge(self, token, amount):
return {"status": "AUTHORIZED", "id": "ch_1"}
 
class FakeOrderRepo:
def get(self, order_id):
return {"id": order_id, "amount_minor": 4200, "state": "NEW"}
 
def save(self, order):
self.saved = order
 
def test_confirm_order():
repo = FakeOrderRepo()
service = OrderService(repo, FakePaymentClient())
app = create_app(service)
 
response = app.test_client().post(
"/orders/ord_1/confirm",
json={"payment_token": "tok_123"},
)
 
assert response.status_code == 200
assert response.json["state"] == "CONFIRMED"

Two fake classes exist only for this file. Both hard-code a payload shape that nothing verifies against the real Stripe or Postgres response.

pytest + unittest.mock1–2 hours
test_orders_pytest.py
tool-assisted
# pytest + unittest.mock + responses.
import pytest
import responses
from unittest.mock import patch
from freezegun import freeze_time
 
@pytest.fixture
def seeded_db(postgresql):
with postgresql.cursor() as cur:
cur.execute(
"INSERT INTO orders VALUES ('ord_1', 4200, 'NEW')"
)
yield postgresql
 
@responses.activate
@freeze_time("2026-09-02T11:04:18Z")
def test_confirm_order(client, seeded_db):
responses.add(
responses.POST,
"https://api.payments.example.com/v1/payment_intents/tok_123/confirm",
json={"status": "AUTHORIZED"},
)
 
response = client.post(
"/orders/ord_1/confirm", json={"payment_token": "tok_123"}
)
 
assert response.status_code == 200
assert response.json()["state"] == "CONFIRMED"

Three testing libraries and a database fixture, and the Stripe body is still a hand-typed literal that will not change when Stripe does.

With Keploy~5 minutes
test-1.yaml
auto-generated
# Recorded with: keploy record -c 'python manage.py runserver'
# Nobody wrote this file. Keploy captured it from a real request.
version: api.keploy.io/v1beta1
kind: Http
name: test-1
spec:
req:
method: POST
url: /orders/ord_1/confirm
body: '{"payment_token":"tok_123"}'
resp:
status_code: 200
body:
id: "ord_1"
state: "CONFIRMED"
amount_minor: 4200
confirmed_at: "2026-09-02T11:04:18Z"
# Varies every run — excluded from assertions automatically.
noise:
- body.confirmed_at
# The real calls this request made, replayed on test runs.
mocks:
- kind: Postgres
operation: "SELECT id, amount_minor, state FROM orders WHERE id = $1"
- kind: Http
url: https://api.payments.example.com/v1/payment_intents/tok_123/confirm
status: 200

The mocks are the actual Postgres rows and the actual Stripe response. Re-recording after an upstream change updates both without editing a line.

Times are estimates for authoring one endpoint’s coverage from scratch, not measurements.

Keploy vs the alternatives

Python testing tools, compared

The options a team on Python actually reaches for, and where each one genuinely wins. Select a row for the full comparison.

Best in classStrongPartialNot covered

Assessments reflect each tool’s documented behaviour, not benchmark measurements.

How it works

Record your Python app once, replay it forever

Keploy sits below your Python process at the network layer. It watches the calls your app already makes, then serves them back on replay so tests run with no dependencies attached.

Keploy records a GET call to /api/v1/orders/{id} on a Python service and captures the dependency calls it makes.

An example shape of a captured call. Your own endpoints and dependencies come from your real traffic, so nothing here has to be written by hand.
Mock coverage

What Keploy mocks for Python, with zero config

7 of the 8 dependencies a typical Python service talks to are stubbed from the recording itself — no mock classes, no fixture files, no containers in CI.

Quick start

Your first Python test suite in under five minutes

Every command below runs against your existing Python service. Nothing in your source tree changes.

  1. 1Install the Keploy CLI

    A single binary — no pip package, and nothing added to requirements.txt.

    curl -sSL https://keploy.io/install.sh | bash
  2. 2Record your app

    Pass your normal start command. Keploy runs it and captures both inbound requests and outbound calls.

    keploy record -c "python manage.py runserver 8000"
  3. 3Exercise the endpoints you care about

    curl, HTTPie, your frontend, or an existing smoke script — every request becomes a test case with its mocks attached.

    curl -X POST localhost:8000/orders/ord_1/confirm -H 'Content-Type: application/json' -d '{"payment_token":"tok_123"}'
  4. 4Replay in CI

    Replay serves the recorded dependency responses, so the job needs no Postgres or Redis service.

    keploy test -c "python manage.py runserver 8000" --delay 10

Ready to try it on your own Python service?

Ecosystem

Works with the rest of your Python stack

Keploy records at the network layer, so framework and driver choices inside your Python app do not change how it captures traffic.

FAQ

Python testing with Keploy: common questions

Join our GlobalCommunity

Connect with developers worldwide. Follow updates, ask questions, share feedback, and ship faster with other Keploy builders.

1.2M+Installs
18.4K+GitHub
100K+Devs
300M+Mocks
1K+Contributors
#1OSS Trending
4.9★★★★★from 500+ reviews onG2GartnerVS CodeChrome
★★★★★

Best report of integration and API tests I've seen — which we don't get from RestAssured.

G2
★★★★★

Future of microservices testing. I don't write tests now!

G2 · 5/5
★★★★★

An amazing product that simplifies the automation.

Gartner · 4.0
XGitHubSlackYouTubeLinkedIn
Built by developers, for developers.Let's build the future, together.