Skip to main content

pytest-pgtap

What is pytest-pgtap?

pytest-pgtap is a pytest plugin for running pgTAP tests against PostgreSQL. pgTAP is a mature and sadly underappreciated Postgres extension that enables running unit tests for database constructs in the database itself. You can use SQL to test your SQL.

You should read the documentation for pgTAP, which describes the functions and strategies for testing Postgres schemas, views, triggers, queries, etc.

Why?

I wanted a to run tests against my Alembic revisions and pgTAP is a great tool, but I have no idea how to install a CPAN package. Anyway, if you are testing a Flask or Django app (or whatever), it's easier to have a single test runner that fits easily into a Python toolchain. Hence a plugin for pytest.

How?

pytest-pgtap provides three entry points into the pgTAP test framework. It is designed to be a replacement for pg_prove, pgTAP's native test runner (which is written in Perl) that can...

  1. Execute SQL Test Scripts directly (test_*.sql files collected by pytest)

  2. Or run xUnit test functions already defined in your database. (i.e runtests())

    ...it can also be used...

  3. As a pytest marker (@pytest.mark.pgtap) on pytest test cases to allow defining pgTAP tests in Python test files alongside application tests.

Requirements

  • Python 3.10+
  • pytest
  • PostgreSQL
  • pgTAP extension installed in the target database

pytest-pgtap does not automatically install pgTAP. Install it in your Postgres instance first.

Installation

Install from PyPI:

pip install pytest-pgtap

Note: To report PgTAP results, this plugin relies on the Pytest subtests feature introduced in Pytest v9. If you are using Pytest 8, you'll need to install the optional subtests dependency, that adds pytest-subtests as an extra plugin for backwards-compatibility.

pip install pytest-pgtap[subtests]

Usage

Connection

  • Set a connection URI explicitly:
pytest --pgtap-uri postgresql://user:pass@host:5432/dbname
  • Or rely on standard libpq environment variables (PGHOST, PGPORT, PGDATABASE, PGUSER, PGPASSWORD).

  • Or override the pgtap_connection fixture in your conftest.py to supply a connection directly — useful when your database is itself managed by a fixture (e.g. testcontainers):

import pytest
import psycopg

@pytest.fixture(scope='session')
def pgtap_connection(postgres):  # postgres is your own session-scoped fixture
    with psycopg.connect(postgres.get_connection_url()) as conn:
        yield conn

Mode 1: SQL file tests

Any file named test*.sql is collected as a pytest item.

Example tests/test_schema.sql:

BEGIN;
SELECT plan(2);
SELECT has_table('public', 'users', 'users table exists');
SELECT has_column('public', 'users', 'email', 'users.email exists');
SELECT * FROM finish();
ROLLBACK;

Run:

pytest --pgtap-uri postgresql://user:pass@host:5432/dbname

Mode 2: xUnit runtests() mode

Run pgTAP xUnit functions from a schema using pgTAP runtests():

pytest --pgtap-uri postgresql://user:pass@host:5432/dbname --pgtap-schema mytests

Optional regex function filter:

pytest --pgtap-uri postgresql://user:pass@host:5432/dbname --pgtap-schema mytests --pgtap-match '^test_'

Mode 3: Inline Python marker mode

Use @pytest.mark.pgtap on a Python test function that returns SQL assertions:

import pytest


@pytest.mark.pgtap
def test_contacts_table():
    return [
        "SELECT has_table('public', 'contacts', 'contacts table exists');",
        "SELECT has_column('public', 'contacts', 'name', 'contacts.name exists');",
    ]

Returning a single SQL string is also supported.

CLI options

  • --pgtap-uri: Postgres connection URI (defaults to DATABASE_URL if set)
  • --pgtap-schema: Enable xUnit mode by running runtests(<schema>)
  • --pgtap-match: Regex filter used with --pgtap-schema

Development

This project suggests Hatch for managing your environment and automating development tasks:

hatch run dev:format
hatch run dev:lint
hatch run dev:test
hatch run docs:build

Similar to tox, Hatch supports multi-version matrix testing so we can verify this library works across different versions of Python, Pytest and Postgres.

hatch run test:run  # run all combinations
hatch -e test.py3.13-pytest9-pg18 run  # single combination
hatch run test:run -- -x  # pass extra args to pytest

The test suite uses testcontainers to spin up Postgres. You'll need Docker or some other OCI runtime.

Using Podman instead of Docker

If you use Podman, enable the compatibility socket and set two environment variables:

systemctl --user enable --now podman.socket
export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/podman/podman.sock
export TESTCONTAINERS_RYUK_DISABLED=true

Release files for pytest-pgtap 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pytest-pgtap 0.3.0
File Size Uploaded
pytest_pgtap-0.3.0.tar.gz 9.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pytest-pgtap 0.3.0
File Interpreter ABI Platform
pytest_pgtap-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 19.2 kB

Release files / pytest_pgtap-0.3.0.tar.gz

Download URL pytest_pgtap-0.3.0.tar.gz
Size 9.4 kB
Tags Source
SHA-256 checksum
How to use checksums
3d5279d46a42fc25592e6940ce05e7733fa8c3df5bba8140a6dff09d7cb2262e
BLAKE2b-256 checksum
How to use checksums
1ccbb2845219899f788952359ae9b71ab7ac6d02fb464168eccb68610ffd4b25
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.13

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 May 29, 2026.

Transparency log

Release files / pytest_pgtap-0.3.0-py3-none-any.whl

Download URL pytest_pgtap-0.3.0-py3-none-any.whl
Size 9.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
816f1276e7ac4223104837f6d40ec8093e04969b9b9499e3c56fc0385e2a8409
BLAKE2b-256 checksum
How to use checksums
e1e4d79dffd5235e37c11cae1ccd88a235f3969ee95962752fe8fdf30bcbf8ec
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.13

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 May 29, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.3.0 This release

2 release files

0.2.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page