Keploy logo
Django logo

Keploy as a Django testing framework

Keploy tests Django applications by recording real requests through the running server and every ORM query, cache read, and outbound HTTP call they trigger, then replaying all of it as assertions. Django's test runner never creates a database, because there is no test database to create.

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

What Keploy gives a Django team

Django 3.2 and above, unchanged

Keploy starts the app through runserver or gunicorn. Function views, class-based views, Django REST Framework, and Django Ninja all record identically because capture happens at the socket.

  • Django 3.2 and above
  • runserver or gunicorn
  • DRF viewsets and serializers
  • No TestCase subclass needed
The problem

Why Django 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 Django: by hand, with Django TestCase + test Client, or with Keploy

Django's test runner buys its fidelity with a test database, a full migration run, and a fixture layer. Keploy captures the SQL your ORM emitted instead, so replay never opens a connection.

Select any row for the full comparison, with code.

Same coverage, three costs

What you write for Django 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: the view is called directly with a fake request.
from django.test import RequestFactory
from orders.views import confirm_order
 
class FakeQuerySet:
def get(self, **kwargs):
return FakeOrder(id="ord_1", amount_minor=4200, state="NEW")
 
def test_confirm_order(monkeypatch):
monkeypatch.setattr("orders.models.Order.objects", FakeQuerySet())
monkeypatch.setattr(
"orders.payments.charge",
lambda token: {"status": "AUTHORIZED"},
)
 
request = RequestFactory().post(
"/orders/ord_1/confirm",
data={"payment_token": "tok_123"},
content_type="application/json",
)
 
response = confirm_order(request, "ord_1")
 
assert response.status_code == 200
assert response.data["state"] == "CONFIRMED"

Two monkeypatches against import paths, and a request built by hand. Middleware, authentication, and the ORM are all absent from what this proves.

Django TestCase + test Client1–2 hours
test_orders_django.py
tool-assisted
# Django TestCase + test Client + responses.
import responses
from django.test import TestCase
from orders.models import Order
 
class ConfirmOrderTests(TestCase):
# Loaded into the test database before every test method.
fixtures = ["orders_seed.json"]
 
def setUp(self):
Order.objects.create(
id="ord_1", amount_minor=4200, state="NEW"
)
 
@responses.activate
def test_confirm_order(self):
responses.add(
responses.POST,
"https://api.payments.example.com/v1/payment_intents/tok_123/confirm",
json={"status": "AUTHORIZED"},
)
 
response = self.client.post(
"/orders/ord_1/confirm",
data={"payment_token": "tok_123"},
content_type="application/json",
)
 
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json()["state"], "CONFIRMED")

Correct and idiomatic — but it needs a test database, every migration applied, a fixture file, and a Stripe body typed out by hand.

With Keploy~5 minutes
test-1.yaml
auto-generated
# Recorded with: keploy record -c 'python manage.py runserver'
# No test database. No migrations. No fixtures.
version: api.keploy.io/v1beta1
kind: Http
name: test-1
spec:
req:
method: POST
url: /orders/ord_1/confirm
header:
Content-Type: application/json
Authorization: Token <captured>
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"
noise:
- body.confirmed_at
# The ORM query as the SQL it emitted, plus the real Stripe call.
mocks:
- kind: Postgres
operation: 'SELECT "orders"."id", "orders"."amount_minor" FROM "orders" WHERE "orders"."id" = $1'
- kind: Http
url: https://api.payments.example.com/v1/payment_intents/tok_123/confirm
status: 200

The mock is the SQL the ORM actually generated, so a query change caught by a re-record shows up as a real diff instead of a passing fixture.

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

Keploy vs the alternatives

Django 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 Django app once, replay it forever

Keploy sits below your Django 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 Django 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 Django, with zero config

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

Quick start

Your first Django test suite in under five minutes

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

  1. 1Install the Keploy CLI

    A standalone binary — nothing is added to requirements.txt and no settings change is needed.

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

    Use runserver locally or gunicorn to match production more closely. Either records identically.

    keploy record -c "python manage.py runserver 8000"
  3. 3Exercise your endpoints

    Each request becomes a test case with the ORM queries and upstream HTTP calls it triggered attached.

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

    No services: block, no migrate step — replay answers every database call from the recording.

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

Ready to try it on your own Django service?

Ecosystem

Works with the rest of your Django stack

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

FAQ

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