Spin up an isolated PostgreSQL test database (reusing local PG or Docker) with zero external service requirements for your test run.
Project description
🧪 testing-containers
Lightweight Python utilities for running ephemeral Docker containers in tests. Includes
TestingPostgresfor disposable PostgreSQL test databases andDockerContainerfor ad-hoc containers.
- Overview
- Design Principles
- Installation
- Usage
- Why use this
- Requirements
- Inspiration & Motivation
- Contributing
🚀 Overview
testing-containers helps you run real services inside Docker for integration or functional tests — without ever needing to manually start external databases, message brokers or anything else.
It provides:
TestingPostgres— a PostgreSQL-specific helper that automatically creates a fresh empty test database before tests start and tears it down afterwards.DockerContainer— a generic helper to start, stop, and execute commands inside any Docker container (Postgres, Redis, LocalStack, etc.).
The goal is simple:
Make your tests fully isolated, reproducible, and environment-agnostic — no shared state, no external dependencies.
🧭 Design Principles
- 🧩 No external services need to be manually started
- 🔁 Fresh empty databases are created every test run
- 🧹 Automatic cleanup when tests finish
- 🧱 No pollution of your dev DB — tests never touch your development data
- ⚙️ Consistent environments — use the same database version as production via Docker
- 🧰 Generic & extensible — same approach works for PostgreSQL, Redis, LocalStack, RabbitMQ, etc.
- 🐳 Only dependency: Docker (required only when containers are used)
⚙️ Installation
pip install testing-containers
Requires Docker installed and running if you plan to spin up containerized services.
💡 Usage
TestingPostgres
You can use TestingPostgres in two ways
a) Run Postgres inside Docker
import psycopg
from testing_containers import TestingPostgres, ContainerOptions
pg = TestingPostgres(
options=ContainerOptions(
namespace="myproject-name" # optional – you can add a namespace to the container
name="testdb" # optional – you can give a name to the container
image="postgres:15.6" # optional / default postgres:16.3
# The following options defines what should happen on stop()
# container will stop or not
# container will be removed or not
# (you can decide on the speed you want on test startup and teardown)
should_stop=True # optional / default=False
remove_on_stop=True # optional / default=False
)
) # spins up a postgres:16.3 container
testdb = pg.postgres.testdb # connection info for your test DB
# Connect and run migrations or tests
conn = psycopg.connect(
dbname=testdb.name,
user=testdb.user,
password=testdb.password,
host=testdb.host,
port=testdb.port,
)
print("Connected:", conn)
# After tests
pg.stop()
b) Connect to an existing Postgres instance
❗ Important: In case the provided database is not available/ready it will spin a postgres container and use that as a fallback
from testing_containers import TestingPostgres, DBConfig
dev_db_config = DBConfig(
host="localhost",
name="dev_db",
user="postgres",
password="secret",
port=5432,
)
pg = TestingPostgres(db_config=dev_db_config)
print(pg.testdb) # e.g. "test_dev_db" — a fresh copy created on the fly
# ... run tests ...
pg.stop() # drops the test DB
✅ Each run creates a temporary database (test_<original_dbname>) and destroys it afterwards.
Example: using pytests, alembic and settings on conftest
- You run
TestingPostgres - You mock DB environment variables to the one of
TestingPostgres().postgres.test_db - So your app during tests runtime will be connected to testdb
- Create a pytest fixture which run alembic migration on start and stops testdb(drops testdb) on teardown
import os
import pytest
from unittest.mock import patch
from alembic import command
from alembic.config import Config
from testing_containers import TestingPostgres
testing = TestingPostgres()
testdb = testing.postgres.test_db
env_vars = {
"DB__USER": testdb.db.user,
"DB__PASSWORD": testdb.db.password,
"DB__HOST": testdb.db.host,
"DB__PORT": str(testdb.db.port),
"DB__NAME": testdb.db.name,
}
with patch.dict(os.environ, env_vars):
from app.settings import settings
@pytest.fixture(scope="session", autouse=True)
def setup_test_db():
"""Setup and teardown for the test database"""
# Run Alembic migrations
alembic_cfg = Config("alembic.ini") # Ensure the path to alembic.ini is correct
try:
command.upgrade(alembic_cfg, "head")
except Exception:
raise
yield # Run tests
# Cleanup: Stop and remove the test DB
testdb.stop()
Generic DockerContainer
Start any service container on demand — e.g. Redis:
from testing_containers import DockerContainer
redis = DockerContainer(
container_name="test-redis",
image="redis:7",
expose_ports=["6379:6379"]
)
redis.start_container()
result = redis.exec(["redis-cli", "ping"])
print(result.stdout.strip()) # → PONG
redis.stop_container()
redis.remove_container()
✅ Great for spinning up ad-hoc containers for any dependency during tests.
🧠 Why use this
| Problem | Solution |
|---|---|
| 🧩 Tests depend on manually started external services | TestingPostgres and DockerContainer spin up Docker containers automatically for your tests. |
| 🧹 Test data pollutes your development database | Each test run uses a fresh, isolated test database, which is dropped when tests finish. |
| ⚙️ Local database version differs from production | Run your tests inside Docker using the same version as production (e.g. postgres:16.3). |
| 🧱 CI/CD pipelines need reproducible environments | Works seamlessly in CI — no extra setup; containers are created and torn down automatically. |
| 🚀 You need Redis, LocalStack, or any other service | DockerContainer can run any Docker image, not just databases. |
| 🧪 You want clean, reliable integration tests | Ensures tests always start from a known empty state — no shared data, no side effects. |
🧪 Requirements
- Python 3.10+
- Docker (required only for containerized tests)
💡 Inspiration and Motivation
This project was inspired by the excellent testing.postgresql package,
which provides temporary PostgreSQL instances for testing.
However, testing.postgresql requires PostgreSQL to be installed locally on the developer’s machine.
That can lead to common issues in real-world teams:
- Developers might have different PostgreSQL versions installed.
- Local PostgreSQL configuration may differ from the production environment.
- Installing or managing Postgres locally can be slow or error-prone in CI and requires additional setup
testing-containers solves these problems by leveraging Docker:
- No local Postgres installation required.
- The same Postgres (or Redis, MariaDB, etc.) version used in production can be pulled and run in tests.
- Works identically on any environment — macOS, Linux, Windows, or CI/CD runners.
In short, it keeps the convenience of testing.postgresql while ensuring environment parity and zero setup.
🧾 License
MIT © Tedi Cela
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file testing_containers-0.3.0.tar.gz.
File metadata
- Download URL: testing_containers-0.3.0.tar.gz
- Upload date:
- Size: 11.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c99d20237c48dc06fb7eea2a70ba4f4217561746b012402d399484ce4332160a
|
|
| MD5 |
795fe4188134c1658d7c68501b9b4a9a
|
|
| BLAKE2b-256 |
d4518976a892997347f676c15e7504bac5648c657ad71b47f350f3a2f1d35f9e
|
Provenance
The following attestation bundles were made for testing_containers-0.3.0.tar.gz:
Publisher:
release.yml on tedicela/testing-containers
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
testing_containers-0.3.0.tar.gz -
Subject digest:
c99d20237c48dc06fb7eea2a70ba4f4217561746b012402d399484ce4332160a - Sigstore transparency entry: 638856356
- Sigstore integration time:
-
Permalink:
tedicela/testing-containers@14fa9b1b52949e3b0fa8beca17cacb2a6b6c8731 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/tedicela
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@14fa9b1b52949e3b0fa8beca17cacb2a6b6c8731 -
Trigger Event:
push
-
Statement type:
File details
Details for the file testing_containers-0.3.0-py3-none-any.whl.
File metadata
- Download URL: testing_containers-0.3.0-py3-none-any.whl
- Upload date:
- Size: 10.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
397ec9369d6c839fae85483e4c8f4e81c6f44d4b001ade50bcfd6df42900b95d
|
|
| MD5 |
bc5173fb10d231b19b23781b297684bd
|
|
| BLAKE2b-256 |
6938fca5f6181d5d00fcbd404879154db300e191df4dff4ce746256a5cae443a
|
Provenance
The following attestation bundles were made for testing_containers-0.3.0-py3-none-any.whl:
Publisher:
release.yml on tedicela/testing-containers
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
testing_containers-0.3.0-py3-none-any.whl -
Subject digest:
397ec9369d6c839fae85483e4c8f4e81c6f44d4b001ade50bcfd6df42900b95d - Sigstore transparency entry: 638856383
- Sigstore integration time:
-
Permalink:
tedicela/testing-containers@14fa9b1b52949e3b0fa8beca17cacb2a6b6c8731 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/tedicela
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@14fa9b1b52949e3b0fa8beca17cacb2a6b6c8731 -
Trigger Event:
push
-
Statement type: