Skip to content

Latest commit

 

History

History
274 lines (207 loc) · 6.66 KB

File metadata and controls

274 lines (207 loc) · 6.66 KB

Quick Start Guide

Get started with Testcontainers for Zig in 5 minutes.

Prerequisites

  • Zig 0.15.2 installed
  • Docker or Docker Desktop installed and running

Installation

Add to your build.zig.zon:

.dependencies = .{
    .testcontainers = .{
        .url = "git+https://github.com/dragosv/testcontainers-zig?ref=main#<commit>",
        .hash = "<hash>",
    },
},

Wire it up in build.zig:

const tc_dep = b.dependency("testcontainers", .{ .target = target, .optimize = optimize });
exe.root_module.addImport("testcontainers", tc_dep.module("testcontainers"));

Basic Example

const std = @import("std");
const tc  = @import("testcontainers");

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const alloc = gpa.allocator();

    // Start a PostgreSQL container using the built-in module.
    var provider = try tc.DockerProvider.init(alloc);
    defer provider.deinit();

    const pg = try tc.modules.postgres.run(&provider, tc.modules.postgres.default_image, .{
        .username = "user",
        .password = "password",
        .database = "mydb",
    });
    defer pg.terminate() catch {};
    defer pg.deinit();

    const url = try pg.connectionString(alloc);
    defer alloc.free(url);

    std.debug.print("Connected to: {s}\n", .{url});
}

Starting a Generic Container

const ctr = try tc.run(alloc, "nginx:latest", .{
    .exposed_ports  = &.{"80/tcp"},
    .wait_strategy  = tc.wait.forHttp("/"),
});
defer ctr.terminate() catch {};
defer ctr.deinit();

const port = try ctr.mappedPort("80/tcp", alloc);
std.debug.print("nginx running on port {d}\n", .{port});

Using in Zig Tests

const std     = @import("std");
const testing = std.testing;
const tc      = @import("testcontainers");

test "postgres: connection string is non-empty" {
    const alloc = testing.allocator;

    var provider = tc.DockerProvider.init(alloc) catch return error.SkipZigTest;
    defer provider.deinit();

    const pg = try tc.modules.postgres.run(&provider, tc.modules.postgres.default_image, .{});
    defer pg.terminate() catch {};
    defer pg.deinit();

    const url = try pg.connectionString(alloc);
    defer alloc.free(url);

    try testing.expect(url.len > 0);
}

Available Modules

PostgreSQL

const pg = try tc.modules.postgres.run(&provider, tc.modules.postgres.default_image, .{
    .username = "user",
    .password = "pass",
    .database = "testdb",
});
const url = try pg.connectionString(alloc);
// postgres://user:pass@localhost:PORT/testdb

MySQL

const db = try tc.modules.mysql.run(&provider, tc.modules.mysql.default_image, .{
    .username = "user",
    .password = "pass",
    .database = "testdb",
});
const url = try db.connectionString(alloc);
// user:pass@tcp(localhost:PORT)/testdb

Redis

const redis = try tc.modules.redis.run(&provider, tc.modules.redis.default_image, .{});
const url = try redis.connectionString(alloc);
// redis://localhost:PORT

MongoDB

const mongo = try tc.modules.mongodb.run(&provider, tc.modules.mongodb.default_image, .{});
const url = try mongo.connectionString(alloc);
// mongodb://localhost:PORT/

RabbitMQ

const mq = try tc.modules.rabbitmq.run(&provider, tc.modules.rabbitmq.default_image, .{});
const amqp = try mq.amqpURL(alloc);   // amqp://guest:guest@localhost:PORT
const http = try mq.httpURL(alloc);   // http://localhost:MGMT_PORT

MinIO

const minio = try tc.modules.minio.run(&provider, tc.modules.minio.default_image, .{});
const url = try minio.connectionString(alloc);  // http://localhost:PORT

Elasticsearch

const es = try tc.modules.elasticsearch.run(&provider, tc.modules.elasticsearch.default_image, .{});
const url = try es.httpURL(alloc);  // http://localhost:PORT

Kafka

const kafka = try tc.modules.kafka.run(&provider, tc.modules.kafka.default_image, .{});
const brokers = try kafka.brokers(alloc);  // localhost:PORT

LocalStack

const ls = try tc.modules.localstack.run(&provider, tc.modules.localstack.default_image, .{});
const endpoint = try ls.endpointURL(alloc);  // http://localhost:PORT

Common Patterns

Using defer for automatic cleanup

const ctr = try provider.runContainer(alloc, req);
defer ctr.terminate() catch |err| std.log.err("terminate: {}", .{err});
defer ctr.deinit();
// ctr is automatically stopped and removed when the scope exits

Custom wait strategies

const ctr = try tc.run(alloc, "postgres:15", .{
    .exposed_ports = &.{"5432/tcp"},
    .wait_strategy = tc.wait.forAll(&.{
        tc.wait.forPort("5432/tcp"),
        tc.wait.forLog("database system is ready"),
    }),
});

Environment variables

const ctr = try tc.run(alloc, "postgres:15", .{
    .exposed_ports = &.{"5432/tcp"},
    .env = &.{
        "POSTGRES_DB=testdb",
        "POSTGRES_USER=testuser",
        "POSTGRES_PASSWORD=testpass",
    },
    .wait_strategy = tc.wait.forPort("5432/tcp"),
});

Network communication between containers

const net = try tc.network.Network.create(&client, "app-network", alloc);
defer net.remove(&client, alloc) catch {};

const db = try tc.run(alloc, "postgres:15", .{
    .networks        = &.{"app-network"},
    .network_aliases = &.{.{ .network = "app-network", .alias = "database" }},
    .wait_strategy   = tc.wait.forPort("5432/tcp"),
});
defer db.terminate() catch {};
defer db.deinit();

// Other containers on "app-network" can reach Postgres at hostname "database".

Troubleshooting

Docker not reachable

Symptom: Connection refused or error.FileNotFound on socket path.

Solution: Ensure Docker is running:

docker ps

Override the socket path via environment variable if needed:

export DOCKER_HOST=/path/to/docker.sock

Container logs

Fetch stdout/stderr to diagnose startup failures:

const logs = try ctr.logs(alloc);
defer alloc.free(logs);
std.debug.print("{s}\n", .{logs});

Wait strategy timeout

Increase the timeout on the container request:

const ctr = try tc.run(alloc, "myimage:latest", .{
    .wait_strategy       = tc.wait.forPort("8080/tcp"),
    .startup_timeout_ns  = 120 * std.time.ns_per_s,
});

Next Steps

Getting Help