ory/dockertest

repository·v4·Indexed 26 days ago

https://github.com/ory/dockertest

A Go library for simplifying integration tests against third-party services by managing Docker containers. It supports Windows, macOS, and Linux, providing features such as automatic container reuse, pool management via NewPool and NewPoolT, and utilities for waiting for service readiness with Retry. The library allows developers to configure containers with tags and environment variables, build custom images from Dockerfiles using BuildAndRun, and manage Docker networks for container-to-container communication.

Tokens
10.7K
Snippets
30
Records
58
Agent score
89%

What's inside ory/dockertest

  1. Configure GitLab CI for Dockertest

    v4

    When running in GitLab CI with shared runners, use the docker:dind service. Set DOCKER_HOST to tcp://docker:2375 and DOCKER_TLS_CERTDIR to "".

    Important: In your pool.Retry callback, use the environment variable (e.g., $YOUR_APP_DB_HOST) instead of localhost to connect to the database, as the database will be available on the host named docker.

    # GitLab CI snippet
    services:
      - docker:dind
    variables:
      DOCKER_HOST: tcp://docker:2375
      DOCKER_TLS_CERTDIR: ""
      YOUR_APP_DB_HOST: docker
  2. Clean up containers and pools

    v4

    Cleanup strategy depends on how you initialized the pool:

    1. NewPoolT + RunT (Recommended for tests): Cleanup is fully automatic via t.Cleanup. No manual action required.
    2. NewPool + Run (For non-test code): You must call resource.Close(ctx) to release individual containers, or pool.Close(ctx) to release all containers and networks tracked by the pool.
    3. Shared pool in TestMain: If using a single pool for an entire package, call pool.Close(ctx) in TestMain after m.Run().
    // Automatic cleanup (Tests)
    func TestDB(t *testing.T) {
        pool := dockertest.NewPoolT(t, "")
        resource := pool.RunT(t, "postgres", dockertest.WithTag("14"))
        // cleanup happens automatically
    }
    
    // Manual cleanup (Non-test)
    ctx := context.Background()
    pool, _ := dockertest.NewPool(ctx, "")
    defer pool.Close(ctx)
    
    resource, _ := pool.Run(ctx, "postgres", dockertest.WithTag("14"))
    defer resource.Close(ctx)
  3. Quick Start with Dockertest

    v4

    Dockertest allows you to spin up Docker containers for integration testing. You can create a pool using NewPoolT, run containers with RunT, and use Retry to wait for service readiness. Containers can be configured with tags and environment variables.

    package myapp_test
    
    import (
        "testing"
        "time"
    
        dockertest "github.com/ory/dockertest/v4"
    )
    
    func TestPostgres(t *testing.T) {
        pool := dockertest.NewPoolT(t, "")
    
        // Container is automatically reused across test runs based on "postgres:14".
        postgres := pool.RunT(t, "postgres",
            dockertest.WithTag("14"),
            dockertest.WithEnv([]string{
                "POSTGRES_PASSWORD=secret",
                "POSTGRES_DB=testdb",
            }),
        )
    
        hostPort := postgres.GetHostPort("5432/tcp")
        // Connect to postgres://postgres:secret@hostPort/testdb
    
        // Wait for PostgreSQL to be ready
        err := pool.Retry(t.Context(), 30*time.Second, func() error {
            // try connecting...
            return nil
        })
        if err != nil {
            t.Fatalf("Could not connect: %v", err)
        }
    }
  4. Migrate from v3 to v4

    v4

    When upgrading to v4, several breaking changes and new patterns are introduced. Key changes include:

    • Automatic Cleanup: Use NewPoolT(t, ...) instead of NewPool. This automatically handles resource cleanup via t.Cleanup.
    • Context-Aware APIs: Most methods now require a context.Context. Use t.Context() in tests.
    • Functional Options: Replace RunWithOptions with functional options like dockertest.WithTag and dockertest.WithEnv.
    • Retry Pattern: pool.Retry now requires a context and a timeout: pool.Retry(ctx, timeout, func() error { ... }).
    • Cleanup: Replace manual pool.Purge(resource) with pool.Close(ctx) (for non-test code) or rely on NewPoolT for automatic cleanup in tests.
    • Error Handling: Use errors.Is() for checking specific error types.
    // v4 pattern for a single container test
    func TestDB(t *testing.T) {
        pool := dockertest.NewPoolT(t, "")
        resource := pool.RunT(t, "postgres",
            dockertest.WithTag("14"),
            dockertest.WithEnv([]string{"POSTGRES_PASSWORD=secret"}),
        )
        pool.Retry(t.Context(), 30*time.Second, func() error { return db.Ping() })
        _ = resource.GetPort("5432/tcp")
    }
  5. Migrate from dockertest v3 to v4

    v4

    To upgrade to v4, follow these steps:

    1. Update dependencies:

      go get github.com/ory/dockertest/v4
      go mod tidy
    2. Update imports: Replace github.com/ory/dockertest/v3 with github.com/ory/dockertest/v4 in all Go files.

    3. Convert API calls:

      • NewPool("") $\rightarrow$ NewPoolT(t, "") (for tests) or NewPool(ctx, "") (outside tests).
      • Run()/RunWithOptions() $\rightarrow$ RunT(t, ...) using functional options.
      • pool.Purge(resource) $\rightarrow$ Automatic cleanup via NewPoolT, or call pool.Close(ctx) in TestMain.
      • pool.Retry(fn) $\rightarrow$ pool.Retry(ctx, timeout, fn).
    4. Test: Run go test ./... to verify the migration.

    go get github.com/ory/dockertest/v4
    go mod tidy
  6. Manage container reuse

    v4

    Containers are automatically reused based on the repository:tag pattern. Each Run/RunT call increments a reference count, and each Close/cleanup decrements it. The container is only removed from Docker when the last reference is released. To force a new container every time, use WithoutReuse().

    // First test creates container
    r1 := pool.RunT(t, "postgres", dockertest.WithTag("14"))
    
    // Second test reuses the same container
    r2 := pool.RunT(t, "postgres", dockertest.WithTag("14"))
    
    // Disable reuse
    resource := pool.RunT(t, "postgres",
        dockertest.WithTag("14"),
        dockertest.WithoutReuse(),
    )
  7. Migrate from v3 to v4

    v4
    Version 4 introduces automatic container reuse to speed up tests and uses a lightweight Docker client to reduce dependencies. For a complete migration guide, refer to the UPGRADE.md file in the repository.
  8. Create a new Docker resource pool

    v4

    Use NewPool to create a pool for managing Docker resources. The endpoint parameter must be empty; instead, configure the Docker connection using the DOCKER_HOST, DOCKER_TLS_VERIFY, and DOCKER_CERT_PATH environment variables, or provide a custom client via the WithMobyClient option.

    If you use NewPool, you are responsible for calling Close(ctx) to clean up all tracked containers and networks and to close the underlying Docker client.

    ctx := context.Background()
    // endpoint must be empty
    pool, err := dockertest.NewPool(ctx, "")
    if err != nil {
    	panic(err)
    }
    // Ensure resources are cleaned up
    defer pool.Close(ctx)
  9. Create a pool for testing with automatic cleanup

    v4
    Use NewPoolT within a test to create a pool that automatically registers its own cleanup with the provided TestingTB (e.g., *testing.T). The returned Pool does not expose Close or CloseT because the lifecycle is managed by the test framework.
  10. Troubleshoot common Dockertest issues

    v4

    If you encounter issues, check the following common solutions:

    IssueSolution
    Container not found errorsUse pool.Close(ctx) in TestMain instead of manual Purge()
    Timeout errorsIncrease timeout using dockertest.WithMaxWait(5 * time.Minute)
    Context canceled errorsUse t.Context() or context.Background() appropriately
    Image pull failuresCheck with errors.Is(err, dockertest.ErrImagePullFailed)
  11. Configure container reuse with WithReuseID

    v4

    By default, v4 reuses containers based on the repository:tag combination. If you need to run multiple containers using the same image but with different configurations (e.g., different environment variables), you must provide a unique ID using dockertest.WithReuseID to prevent them from silently returning the same container.

    db1 := pool.RunT(t, "postgres",
        dockertest.WithTag("14"),
        dockertest.WithEnv([]string{"POSTGRES_DB=db1"}),
        dockertest.WithReuseID("postgres-db1"),
    )
    
    db2 := pool.RunT(t, "postgres",
        dockertest.WithTag("14"),
        dockertest.WithEnv([]string{"POSTGRES_DB=db2"}),
        dockertest.WithReuseID("postgres-db2"),
    )