Testcontainers spins up real Docker containers for your integration tests — PostgreSQL, Redis, Kafka, Elasticsearch, anything with a Docker image. No mocks, no test doubles, just real services.
Why Testcontainers?
- Real services — test against actual Postgres, Redis, Kafka
- No mocks — catches integration bugs that mocks miss
- Automatic cleanup — containers destroyed after tests
- Multi-language — Java, Go, Python, Node.js, Rust, .NET
- Testcontainers Cloud — no local Docker needed
Python
pip install testcontainers
from testcontainers.postgres import PostgresContainer
from testcontainers.redis import RedisContainer
import psycopg2
import redis
def test_postgres():
with PostgresContainer("postgres:16") as postgres:
conn = psycopg2.connect(postgres.get_connection_url())
cur = conn.cursor()
cur.execute("CREATE TABLE users (id SERIAL, name TEXT)")
cur.execute("INSERT INTO users (name) VALUES ('Alice')")
cur.execute("SELECT name FROM users")
assert cur.fetchone()[0] == 'Alice'
conn.close()
def test_redis():
with RedisContainer("redis:7") as redis_container:
client = redis.Redis(
host=redis_container.get_container_host_ip(),
port=redis_container.get_exposed_port(6379)
)
client.set('key', 'value')
assert client.get('key') == b'value'
Node.js
npm install testcontainers
import { PostgreSqlContainer } from '@testcontainers/postgresql';
import { RedisContainer } from '@testcontainers/redis';
import pg from 'pg';
import { createClient } from 'redis';
describe('Integration tests', () => {
test('PostgreSQL', async () => {
const container = await new PostgreSqlContainer().start();
const client = new pg.Client({ connectionString: container.getConnectionUri() });
await client.connect();
await client.query('CREATE TABLE users (id SERIAL, name TEXT)');
await client.query("INSERT INTO users (name) VALUES ('Alice')");
const result = await client.query('SELECT name FROM users');
expect(result.rows[0].name).toBe('Alice');
await client.end();
await container.stop();
});
test('Redis', async () => {
const container = await new RedisContainer().start();
const client = createClient({ url: container.getConnectionUrl() });
await client.connect();
await client.set('key', 'value');
expect(await client.get('key')).toBe('value');
await client.disconnect();
await container.stop();
});
});
Go
package main_test
import (
"context"
"testing"
"github.com/testcontainers/testcontainers-go"
"github.com/testcontainers/testcontainers-go/modules/postgres"
"github.com/jackc/pgx/v5"
)
func TestPostgres(t *testing.T) {
ctx := context.Background()
pgContainer, err := postgres.Run(ctx, "postgres:16",
postgres.WithDatabase("test"),
postgres.WithUsername("test"),
postgres.WithPassword("test"),
)
if err != nil {
t.Fatal(err)
}
defer pgContainer.Terminate(ctx)
connStr, _ := pgContainer.ConnectionString(ctx, "sslmode=disable")
conn, _ := pgx.Connect(ctx, connStr)
defer conn.Close(ctx)
_, err = conn.Exec(ctx, "CREATE TABLE users (id SERIAL, name TEXT)")
if err != nil {
t.Fatal(err)
}
var name string
conn.Exec(ctx, "INSERT INTO users (name) VALUES ('Alice')")
conn.QueryRow(ctx, "SELECT name FROM users").Scan(&name)
if name != "Alice" {
t.Errorf("expected Alice, got %s", name)
}
}
Available Modules
| Module | Image |
|---|---|
| PostgreSQL | postgres:16 |
| MySQL | mysql:8 |
| Redis | redis:7 |
| Kafka | confluentinc/cp-kafka |
| Elasticsearch | elasticsearch:8 |
| MongoDB | mongo:7 |
| RabbitMQ | rabbitmq:3 |
| MinIO | minio/minio |
| Localstack | localstack/localstack |
Resources
Need testing tools or data automation? Check my Apify actors or email spinov001@gmail.com.
Top comments (0)