dockertesting

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 22, 2026 License: Apache-2.0 Imports: 15 Imported by: 0

README

dockertesting

A Go library for running go test inside Docker containers using testcontainers-go.

Why

Some tests require capabilities that are difficult or impossible to achieve when running directly on the host:

  • Custom DNS resolution so containers can reach each other by hostname
  • Nested testcontainers where tests spin up their own Postgres, Redis, etc. and need to communicate with them via the same Docker network
  • Isolated environments that do not affect the host

This library wraps your Go package in a Docker container, runs the tests inside, and returns the results. All Docker resources (networks, containers) are cleaned up automatically.

Install

go get github.com/djosh34/dockertesting

Basic Usage

Run tests for a package inside a Docker container:

// From integration_test.go TestRun_SimplePackage
packagePath, _ := filepath.Abs("testdata/simple")
result, err := dockertesting.Run(ctx, packagePath)
if err != nil {
    log.Fatal(err)
}
fmt.Printf("Exit code: %d\n", result.ExitCode)

DNS Aliases

Make the test container reachable via custom hostnames within the Docker network. This is useful when tests need to connect to themselves or other services via specific DNS names.

// From integration_test.go TestRun_DNSAlias
packagePath, _ := filepath.Abs("testdata/dnsalias")
result, err := dockertesting.Run(ctx, packagePath, dockertesting.WithAliases("myapp.test"))

The test inside the container can then make HTTP requests to http://myapp.test:port/ and the DNS will resolve correctly within the Docker network.

Nested Testcontainers

When your tests use testcontainers-go internally to spin up additional containers (databases, message queues, etc.), you need two things:

  1. Docker socket access so testcontainers-go can create containers
  2. The network name so nested containers attach to the same network

Enable this with WithVarSock():

// From integration_test.go TestRun_NestedTestcontainers
packagePath, _ := filepath.Abs("testdata/nested")
result, err := dockertesting.Run(ctx, packagePath, dockertesting.WithVarSock())

When WithVarSock() is enabled:

  • The Docker socket (/var/run/docker.sock) is mounted into the container
  • The TESTCONTAINERS_DOCKER_NETWORK environment variable is set to the network name

Your nested tests read this environment variable to attach their containers to the same network:

// From testdata/nested/nested_test.go TestNestedContainer
networkName := os.Getenv("TESTCONTAINERS_DOCKER_NETWORK")
if networkName == "" {
    t.Skip("TESTCONTAINERS_DOCKER_NETWORK not set")
}

nginxContainer, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
    ContainerRequest: testcontainers.ContainerRequest{
        Image:    "nginx:alpine",
        Networks: []string{networkName},
        NetworkAliases: map[string][]string{
            networkName: {"nested-service.test"},
        },
    },
    Started: true,
})

This allows the outer container (running your tests) and the inner container (nginx in this example) to communicate via DNS aliases.

Options

All options use the functional options pattern and can be combined:

result, err := dockertesting.Run(ctx, packagePath,
    dockertesting.WithPattern("./..."),
    dockertesting.WithArgs("-v", "-race"),
    dockertesting.WithAliases("myapp.test", "api.local"),
    dockertesting.WithVarSock(),
    dockertesting.WithTimeout(5 * time.Minute),
)

WithPattern

Set the test pattern passed to go test. Defaults to ./....

dockertesting.WithPattern("./api/...")

WithArgs

Pass additional arguments to go test. Multiple calls are cumulative.

dockertesting.WithArgs("-v", "-race", "-count=1")

WithAliases

Add DNS aliases for the container. Multiple calls are cumulative. Other containers on the same network can reach this container using these hostnames.

dockertesting.WithAliases("myapp.test", "api.local")

WithVarSock

Mount the Docker socket into the container. Required when tests use testcontainers-go or otherwise need Docker access. Also sets TESTCONTAINERS_DOCKER_NETWORK env var.

dockertesting.WithVarSock()

WithSockPath

Override the Docker socket path on the host. Only relevant when using WithVarSock(). Defaults to /var/run/docker.sock.

dockertesting.WithSockPath("/custom/docker.sock")

WithTimeout

Set the maximum duration for the entire test execution. Defaults to 10 minutes.

dockertesting.WithTimeout(5 * time.Minute)

Result

The Run function returns a Result struct:

type Result struct {
    Stdout   []byte  // Combined stdout/stderr from test execution
    Coverage []byte  // Coverage profile bytes from -coverprofile
    ExitCode int     // Exit code from go test (0 = success)
}

Coverage Retrieval

Coverage is automatically collected via -coverprofile and returned in Result.Coverage. The coverage file is written to /tmp/coverage.txt inside the container and copied out after test execution.

Real-time Output

Stdout and stderr are forwarded to os.Stdout and os.Stderr in real-time during test execution. The output is also captured and returned in Result.Stdout.

Cleanup

All Docker resources are cleaned up automatically via deferred cleanup functions, regardless of success or failure. No manual cleanup is required.

Timeout Handling

When a timeout occurs, the error returned is wrapped as a TimeoutError:

result, err := dockertesting.Run(ctx, packagePath, dockertesting.WithTimeout(1*time.Second))
if err != nil {
    var timeoutErr *dockertesting.TimeoutError
    if errors.As(err, &timeoutErr) {
        fmt.Printf("Operation %s timed out\n", timeoutErr.Operation)
    }
}

Run Tests

make help              # show available commands
make test              # run unit tests
make test-integration  # run integration tests (requires Docker)

License

Licensed under the Apache License, Version 2.0. See LICENSE file for details.

Documentation

Overview

Package dockertesting provides a Go library for running go test inside Docker containers.

This library enables running tests for any Go package inside a Docker container with custom DNS aliases, network configuration, and support for nested testcontainers. It is particularly useful when tests need:

  • Custom DNS resolution via container network aliases
  • Tests that spin up their own testcontainers (e.g., PostgreSQL, Redis) while communicating with them via the same Docker network
  • Isolated test environments that don't affect the host

Basic Usage

The main entry point is the Run function which takes a package path and options:

result, err := dockertesting.Run(ctx, "./mypackage")
if err != nil {
    log.Fatal(err)
}
fmt.Printf("Exit code: %d\n", result.ExitCode)
fmt.Printf("Coverage:\n%s\n", result.Coverage)

DNS Aliases

To make the test container accessible via custom DNS names within the network:

result, err := dockertesting.Run(ctx, "./mypackage",
    dockertesting.WithAliases("myapp.test", "api.local"),
)

Nested Testcontainers

For tests that use testcontainers-go internally, enable Docker socket mounting:

result, err := dockertesting.Run(ctx, "./mypackage",
    dockertesting.WithVarSock(),
)

The TESTCONTAINERS_DOCKER_NETWORK environment variable is automatically set in the container, allowing nested testcontainers to attach to the same network.

Additional Options

Configure test patterns, timeouts, and additional go test arguments:

result, err := dockertesting.Run(ctx, "./mypackage",
    dockertesting.WithPattern("./..."),
    dockertesting.WithArgs("-v", "-race"),
    dockertesting.WithTimeout(5 * time.Minute),
)

Results

The Run function returns a Result containing:

  • Stdout: Combined stdout/stderr from the test execution
  • Coverage: The coverage profile bytes (from -coverprofile)
  • ExitCode: The exit code from go test (0 = success)

Output is also streamed to os.Stdout/os.Stderr in real-time during execution.

Cleanup

All Docker resources (networks and containers) are automatically cleaned up regardless of success or failure.

Index

Constants

View Source
const DefaultCoverageFile = "/tmp/coverage.txt"

DefaultCoverageFile is the default path where coverage output is written inside the container.

View Source
const DefaultExecTimeout = 10 * time.Minute

DefaultExecTimeout is the default timeout for test execution.

View Source
const DefaultPattern = "./..."

DefaultPattern is the default test pattern used when none is specified.

View Source
const DefaultSockPath = "/var/run/docker.sock"

DefaultSockPath is the default Docker socket path.

View Source
const DefaultTimeout = 10 * time.Minute

DefaultTimeout is the default timeout for test execution (10 minutes).

Variables

This section is empty.

Functions

This section is empty.

Types

type CreateContainerConfig

type CreateContainerConfig struct {
	// PackagePath is the absolute path to the Go package to test.
	PackagePath string

	// Network is the Docker network to attach the container to.
	Network *DockerNetwork

	// Aliases are DNS aliases for the container within the network.
	Aliases []string

	// EnableVarSock enables mounting the Docker socket into the container.
	EnableVarSock bool

	// SockPath is the path to the Docker socket on the host.
	SockPath string

	// NetworkName is the name of the Docker network (for env var).
	NetworkName string
}

CreateContainerConfig holds the configuration needed to create a test container.

type DockerNetwork

type DockerNetwork struct {
	// Name is the name of the Docker network.
	Name string
	// contains filtered or unexported fields
}

DockerNetwork wraps a testcontainers Docker network and provides access to the network name and cleanup functionality.

func CreateNetwork

func CreateNetwork(ctx context.Context) (*DockerNetwork, func(context.Context) error, error)

CreateNetwork creates a new Docker network using testcontainers-go. The network is created with an auto-generated name and can be used to attach containers.

The caller is responsible for cleaning up the network by calling the cleanup function returned, or by calling network.Remove(ctx).

func (*DockerNetwork) Network

Network returns the underlying testcontainers.DockerNetwork for use with network.WithNetwork() when attaching containers.

func (*DockerNetwork) Remove

func (n *DockerNetwork) Remove(ctx context.Context) error

Remove removes the Docker network. This should be called when the network is no longer needed to clean up resources.

type ExecConfig

type ExecConfig struct {
	// Pattern is the test pattern to run (e.g., "./...").
	Pattern string

	// Args are additional arguments to pass to go test.
	Args []string

	// CoverageFile is the path inside the container where coverage output is written.
	CoverageFile string

	// Timeout is the maximum duration for test execution.
	Timeout time.Duration
}

ExecConfig holds the configuration for executing tests in the container.

type ExecResult

type ExecResult struct {
	// Stdout contains the combined stdout/stderr output from the test execution.
	Stdout []byte

	// ExitCode is the exit code from the test execution.
	// 0 indicates success, non-zero indicates failure.
	ExitCode int
}

ExecResult holds the result of test execution.

type Option

type Option func(*Options)

Option is a functional option for configuring Options.

func WithAliases

func WithAliases(aliases ...string) Option

WithAliases sets DNS aliases for the container within the Docker network. Other containers on the same network can reach this container using these names. This is useful for tests that need to connect to services via custom hostnames.

Multiple calls to WithAliases are cumulative.

Example:

dockertesting.Run(ctx, path, dockertesting.WithAliases("myapp.test", "api.local"))

func WithArgs

func WithArgs(args ...string) Option

WithArgs sets additional arguments to pass to go test. These are appended after the pattern. Common examples include "-v" for verbose output, "-race" for race detection, or "-count=1" to disable test caching.

Multiple calls to WithArgs are cumulative.

Example:

dockertesting.Run(ctx, path, dockertesting.WithArgs("-v", "-race"))

func WithPattern

func WithPattern(pattern string) Option

WithPattern sets the test pattern to run. The pattern is passed to go test and follows the same syntax (e.g., "./...", "./pkg/...", "TestFoo"). If not set, defaults to "./..." to run all tests.

Example:

dockertesting.Run(ctx, path, dockertesting.WithPattern("./api/..."))

func WithSockPath

func WithSockPath(path string) Option

WithSockPath sets the path to the Docker socket on the host. Only relevant when WithVarSock() is also used. Defaults to "/var/run/docker.sock".

Example:

dockertesting.Run(ctx, path,
    dockertesting.WithVarSock(),
    dockertesting.WithSockPath("/custom/docker.sock"),
)

func WithTimeout

func WithTimeout(timeout time.Duration) Option

WithTimeout sets the maximum duration for the entire test execution. If the timeout is reached, the operation will be cancelled and a TimeoutError returned. Defaults to 10 minutes.

Example:

dockertesting.Run(ctx, path, dockertesting.WithTimeout(5 * time.Minute))

func WithVarSock

func WithVarSock() Option

WithVarSock enables mounting the Docker socket into the container. This is required when the tests inside the container use testcontainers-go or otherwise need to interact with Docker.

The socket is mounted from the host's SockPath (default: /var/run/docker.sock) to /var/run/docker.sock inside the container.

When enabled, the TESTCONTAINERS_DOCKER_NETWORK environment variable is also set in the container, allowing nested testcontainers to attach to the same network.

Example:

dockertesting.Run(ctx, path, dockertesting.WithVarSock())

type Options

type Options struct {
	// PackagePath is the path to the Go package to test (required).
	PackagePath string

	// Pattern is the test pattern to run (default: "./...").
	Pattern string

	// Args are additional arguments to pass to go test.
	Args []string

	// Aliases are DNS aliases for the container.
	Aliases []string

	// EnableVarSock enables mounting the Docker socket into the container.
	EnableVarSock bool

	// SockPath is the path to the Docker socket on the host (default: "/var/run/docker.sock").
	SockPath string

	// Timeout is the maximum duration for the entire test execution (default: 10 minutes).
	Timeout time.Duration
}

Options holds the configuration for running tests in a Docker container.

func NewOptions

func NewOptions(packagePath string, opts ...Option) (*Options, error)

NewOptions creates a new Options with the given package path and functional options. It returns an error if the package path is empty.

type Result

type Result struct {
	// Stdout contains the combined stdout/stderr output from the test execution.
	Stdout []byte

	// Coverage contains the coverage file contents as bytes.
	// May be nil if coverage was not generated (e.g., tests failed early).
	Coverage []byte

	// ExitCode is the exit code from the test execution.
	// 0 indicates success, non-zero indicates test failures.
	ExitCode int
}

Result holds the result of running tests in a Docker container.

func Run

func Run(ctx context.Context, packagePath string, opts ...Option) (*Result, error)

Run executes go test for the given package path inside a Docker container.

It creates a Docker network, builds and starts a container with the package, executes the tests with coverage profiling, and returns the results.

The function ensures cleanup of all resources (network and container) regardless of success or failure. Stdout/stderr are forwarded to os.Stdout/os.Stderr in real-time.

Example:

result, err := dockertesting.Run(ctx, "./mypackage",
    dockertesting.WithAliases("myapp.test"),
    dockertesting.WithVarSock(),
)
if err != nil {
    log.Fatal(err)
}
fmt.Printf("Exit code: %d\n", result.ExitCode)
fmt.Printf("Coverage:\n%s\n", result.Coverage)

type TestContainer

type TestContainer struct {
	// contains filtered or unexported fields
}

TestContainer wraps a testcontainers container for running Go tests.

func CreateContainer

func CreateContainer(ctx context.Context, cfg CreateContainerConfig) (*TestContainer, error)

CreateContainer builds and creates a Docker container for running Go tests. The container is built from the package at PackagePath using the embedded Dockerfile template. The container is attached to the provided network with optional aliases.

The container starts with "sleep infinity" to keep it alive for executing tests via Exec.

The caller is responsible for terminating the container by calling Terminate().

func (*TestContainer) Container

func (c *TestContainer) Container() testcontainers.Container

Container returns the underlying testcontainers.Container.

func (*TestContainer) CopyCoverage

func (c *TestContainer) CopyCoverage(ctx context.Context) ([]byte, error)

CopyCoverage is a convenience method that copies the coverage file from the default location. It returns the coverage file contents as bytes, or nil if the file doesn't exist.

func (*TestContainer) CopyCoverageFromPath

func (c *TestContainer) CopyCoverageFromPath(ctx context.Context, coveragePath string) ([]byte, error)

CopyCoverageFromPath copies the coverage file from a custom path inside the container. It returns the coverage file contents as bytes, or nil if the file doesn't exist.

func (*TestContainer) CopyFileFromContainer

func (c *TestContainer) CopyFileFromContainer(ctx context.Context, containerFilePath string) ([]byte, error)

CopyFileFromContainer copies a file from the container and returns its contents as bytes. If the file doesn't exist, it returns nil bytes and a nil error. This is useful for extracting coverage files which may not exist if tests failed early.

func (*TestContainer) ExecTest

func (c *TestContainer) ExecTest(ctx context.Context, cfg ExecConfig) (*ExecResult, error)

ExecTest runs `go test` inside the container and returns the result. The command includes coverage profiling to /tmp/coverage.txt by default.

The method captures stdout/stderr and returns them along with the exit code. A non-zero exit code typically indicates test failures.

func (*TestContainer) Terminate

func (c *TestContainer) Terminate(ctx context.Context) error

Terminate stops and removes the container.

type TimeoutError

type TimeoutError struct {
	Operation string
	Err       error
}

TimeoutError represents an error that occurred due to a timeout.

func (*TimeoutError) Error

func (e *TimeoutError) Error() string

func (*TimeoutError) Unwrap

func (e *TimeoutError) Unwrap() error

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL