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.11+
  • 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

To apply pytest marks to a SQL file, write them as comments before the first line of SQL, using the same syntax as a decorator:

-- pytest.mark.slow
-- pytest.mark.xfail(reason="known bug", strict=True)
BEGIN;
...

Markers apply to the whole file.

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_'

Note: These tests live in the database rather than in a file, so only fixtures defined in the root conftest.py are evaluated.

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(migrated_db):  # Pytest fixture for db setup
    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 run test.py3.13-pytest9-pg18:run  # single combination
hatch run test.py3.13-pytest9-pg18:run -x  # pass extra args to pytest
hatch env show test  # list combinations

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

Metadata

Release files for pytest-pgtap 0.4.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.4.0
File Size Uploaded Provenance
pytest_pgtap-0.4.0.tar.gz 12.2 kB With provenance Details

Built distribution (wheel)

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

Total release size: 26.8 kB

Release files / pytest_pgtap-0.4.0.tar.gz

Download URL pytest_pgtap-0.4.0.tar.gz
Size 12.2 kB
Tags Source
SHA-256 checksum
How to use checksums
d7f72881ee6bfdcaceb2efeb4754f7c717fe3a0bf4ea0a3043f3b857c34f8fb6
BLAKE2b-256 checksum
How to use checksums
4390d3e1579038ee9c1a27bb1aa6a5e86f91ac1b14de07af4be68f2dba78174a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 Oct 21, 2026.

Transparency log

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

Download URL pytest_pgtap-0.4.0-py3-none-any.whl
Size 14.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
49581045f7a3aa862a11d9f1aa70e92021a584e17e2b9d2aea91d7a0eb710fec
BLAKE2b-256 checksum
How to use checksums
748b66d33acbaafdff18d630733d20738ccdf46eadc284a2d3ed0172d7b75d22
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 Oct 21, 2026.

Transparency log

Security for pytest-pgtap 0.4.0

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.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for pytest_pgtap-0.4.0.tar.gz

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 Oct 21, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

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