Keploy logo
PostgreSQL logo

Keploy as a PostgreSQL testing framework

Keploy mocks PostgreSQL by recording its frontend/backend protocol beneath your driver, capturing the exact rows and column types each query returned, then replaying them during tests. No Postgres runs in CI, no schema is migrated, and no in-memory substitute is involved.

Generate PostgreSQL tests free
keploy record -c "gunicorn app.wsgi"
18.4K+VS Code1.2M+300M+mocks created

What Keploy gives a PostgreSQL team

Captured at the frontend/backend protocol

Keploy reads the protocol beneath your driver, so each query replays with the data rows Postgres sent and the RowDescription that described them — type OIDs included.

  • Below pgx, psycopg, node-postgres, JDBC
  • Type OIDs from RowDescription
  • Bind parameters captured per query
  • Transactions and savepoints replay in order
The problem

Why PostgreSQL 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 PostgreSQL: by hand, with Testcontainers Postgres, or with Keploy

Testcontainers gives full Postgres semantics and charges a Docker runtime, an image pull, and a migration run per job. Keploy records that server once and replays its responses.

Select any row for the full comparison, with code.

Same coverage, three costs

What you write for PostgreSQL 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
order_repo_stub.py
hand-written
# Hand-written stub: no SQL is executed, parsed, or type-mapped.
from dataclasses import dataclass
from decimal import Decimal
 
@dataclass
class Order:
id: str
amount: Decimal
state: str
 
class OrderRepoStub:
def __init__(self):
self._rows = {
# A guess: real Postgres numeric may arrive as a string.
"ord_1": Order("ord_1", Decimal("42.00"), "NEW"),
}
 
def get(self, order_id):
return self._rows.get(order_id)
 
def set_state(self, order_id, state):
# No constraint, no trigger, no unique index is checked here.
self._rows[order_id].state = state

The stub decides its own types, so the classic bug — a numeric column arriving as a string — is impossible to reproduce and impossible to catch.

Testcontainers Postgres1–2 hours
test_order_repo.py
tool-assisted
# Testcontainers Postgres: real server, Docker in CI.
import pytest
from testcontainers.postgres import PostgresContainer
 
@pytest.fixture(scope="session")
def pg():
with PostgresContainer("postgres:16") as container:
run_migrations(container.get_connection_url())
seed_orders(container.get_connection_url())
yield container
 
def test_reads_a_seeded_order(pg):
repo = OrderRepo(connect(pg.get_connection_url()))
 
order = repo.get("ord_1")
 
assert order.state == "NEW"
assert order.amount == Decimal("42.00")

Accurate down to type OIDs — and every job pulls a Postgres 16 image, boots a server, and replays the whole migration history first.

With Keploy~5 minutes
mocks.yaml
auto-generated
# Recorded with: keploy record -c 'gunicorn app.wsgi'
# The Postgres response, captured at the protocol level.
version: api.keploy.io/v1beta1
kind: Postgres
name: mock-2
spec:
request:
query: "SELECT id, amount, state FROM orders WHERE id = $1"
params:
- "ord_1"
response:
# RowDescription, exactly as the server sent it.
row_description:
- name: id
type_oid: 25
- name: amount
type_oid: 1700
- name: state
type_oid: 25
data_rows:
- ["ord_1", "42.00", "NEW"]
command_complete: "SELECT 1"

Type OID 1700 is numeric, and the value replays as the string Postgres actually sent — so driver conversion is exercised, not assumed.

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

Keploy vs the alternatives

PostgreSQL testing tools, compared

The options a team on relational databases 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 PostgreSQL app once, replay it forever

Keploy sits below your PostgreSQL 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 PostgreSQL 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

PostgreSQL clients Keploy records, driver by driver

Keploy captures PostgreSQL at the wire protocol, so 6 of these 8 clients need no adapter, no test double, and no PostgreSQL instance in CI.

Quick start

Your first PostgreSQL test suite in under five minutes

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

  1. 1Install the Keploy CLI

    One binary on the recording machine. Nothing is installed into Postgres, and no extension is required.

    curl -sSL https://keploy.io/install.sh | bash
  2. 2Record with your development database reachable

    Run the app as usual. Every query, its bind parameters, and the rows Postgres returned are captured together.

    keploy record -c "gunicorn app.wsgi --bind 0.0.0.0:8000"
  3. 3Exercise the paths that hit the database

    Reads, writes, transactions, and prepared statements all record with their parameters and responses.

    curl localhost:8000/orders/ord_1
    curl -X POST localhost:8000/orders/ord_1/confirm -d '{"payment_token":"tok_123"}'
  4. 4Replay with Postgres stopped

    Shut Postgres down and run the suite. If replay passes, every query your code makes is answered by a recorded mock.

    keploy test -c "gunicorn app.wsgi --bind 0.0.0.0:8000" --delay 12

Ready to try it on your own PostgreSQL service?

Ecosystem

Works with the rest of your PostgreSQL stack

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

FAQ

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