dktest

package module
v0.3.1 Latest Latest
Warning

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

Go to latest
Published: Jun 7, 2019 License: Apache-2.0 Imports: 15 Imported by: 0

README

dktest

Build Status Code Coverage GoDoc Go Report Card GitHub Release Supported Go versions

dktest is short for dockertest.

dktest makes it stupidly easy to write integration tests in Go using Docker. Pulling images, starting containers, and cleaning up (even if your tests panic) is handled for you automatically!

API

Run() is the workhorse

type ContainerInfo struct {
    ID        string
    Name      string
    ImageName string
    IP        string
    Port      string
}

type Options struct {
    Timeout   time.Duration
    ReadyFunc func(ContainerInfo) bool
    Env       map[string]string
    // If you prefer to specify your port bindings as a string, use nat.ParsePortSpecs()
    PortBindings nat.PortMap
    PortRequired bool
}

func Run(t *testing.T, imgName string, opts Options, testFunc func(*testing.T, ContainerInfo))

Example Usage

import (
    "context"
    "testing"
)

import (
    "github.com/dhui/dktest"
    _ "github.com/lib/pq"
)

func pgReady(ctx context.Context, c dktest.ContainerInfo) bool {
    ip, port, err := c.FirstPort()
    if err != nil {
        return false
    }
    connStr := fmt.Sprintf("host=%s port=%s user=postgres dbname=postgres sslmode=disable", ip, port)
    db, err := sql.Open("postgres", connStr)
    if err != nil {
        return false
    }
    defer db.Close()
    return db.PingContext(ctx) == nil
}

func Test(t *testing.T) {
    dktest.Run(t, "postgres:alpine", dktest.Options{PortRequired: true, ReadyFunc: pgReady},
        func(t *testing.T, c dktest.ContainerInfo) {
        ip, port, err := c.FirstPort()
        if err != nil {
            t.Fatal(err)
        }
        connStr := fmt.Sprintf("host=%s port=%s user=postgres dbname=postgres sslmode=disable", ip, port)
        db, err := sql.Open("postgres", connStr)
        if err != nil {
            t.Fatal(err)
        }
        defer db.Close()
        if err := db.Ping(); err != nil {
            t.Fatal(err)
        }
        // Test using db
    })
}

For more examples, see the docs.

Debugging tests

Running go test with the -v option will display the container lifecycle log statements along with the container ID.

Short lived tests/containers

Run go test with the -v option and specify the LogStdout and/or LogStderr Options to see the container's logs.

Interactive tests/containers

Run go test with the -v option to get the container ID and check the container's logs with docker logs -f $CONTAINER_ID.

Cleaning up dangling containers

In the unlikely scenario where dktest leaves dangling containers, you can find and removing them by using the dktest label:

# list dangling containers
$ docker ps -a --filter label=dktest
# stop dangling containers
$ docker ps --filter label=dktest | awk '{print $1}' | grep -v CONTAINER | xargs docker stop
# remove dangling containers
$ docker container prune --filter label=dktest

Roadmap

  • Support multiple ports in ContainerInfo
  • Use non-default network
  • Add more Options
    • Volume mounts
    • Network config
  • Support testing against multiple containers. It can be faked for now by nested/recursive Run() calls but that serializes the containers' startup time.

Comparison to dockertest

Why dktest is better
Why dockertest is better
  • Has been around longer and API is more stable
  • More options for configuring Docker containers
  • Has more Github stars and contributors

Documentation

Overview

Package dktest provides an easy way to write integration tests using Docker

dktest is short for dockertest

Example (Nginx)
package main

import (
	"context"
	"fmt"
	"net/http"
	"net/url"
	"testing"

	"github.com/jjfeiler/dktest"
	_ "github.com/lib/pq"
)

func main() {
	dockerImageName := "nginx:alpine"
	readyFunc := func(ctx context.Context, c dktest.ContainerInfo) bool {
		ip, port, err := c.FirstPort()
		if err != nil {
			return false
		}
		u := url.URL{Scheme: "http", Host: ip + ":" + port}
		req, err := http.NewRequest(http.MethodGet, u.String(), nil)
		if err != nil {
			fmt.Println(err)
			return false
		}
		req = req.WithContext(ctx)
		if resp, err := http.DefaultClient.Do(req); err != nil {
			return false
		} else if resp.StatusCode != 200 {
			return false
		}
		return true
	}

	// dktest.Run() should be used within a test
	dktest.Run(&testing.T{}, dockerImageName, dktest.Options{PortRequired: true, ReadyFunc: readyFunc},
		func(t *testing.T, c dktest.ContainerInfo) {
			// test code here
		})

}
Output:

Example (Postgres)
package main

import (
	"context"
	"database/sql"
	"fmt"
	"testing"

	"github.com/jjfeiler/dktest"
	_ "github.com/lib/pq"
)

func main() {
	dockerImageName := "postgres:alpine"
	readyFunc := func(ctx context.Context, c dktest.ContainerInfo) bool {
		ip, port, err := c.FirstPort()
		if err != nil {
			return false
		}
		connStr := fmt.Sprintf("host=%s port=%s user=postgres dbname=postgres sslmode=disable", ip, port)
		db, err := sql.Open("postgres", connStr)
		if err != nil {
			return false
		}
		defer db.Close() // nolint:errcheck
		return db.PingContext(ctx) == nil
	}

	// dktest.Run() should be used within a test
	dktest.Run(&testing.T{}, dockerImageName, dktest.Options{PortRequired: true, ReadyFunc: readyFunc},
		func(t *testing.T, c dktest.ContainerInfo) {
			ip, port, err := c.FirstPort()
			if err != nil {
				t.Fatal(err)
			}
			connStr := fmt.Sprintf("host=%s port=%s user=postgres dbname=postgres sslmode=disable", ip, port)
			db, err := sql.Open("postgres", connStr)
			if err != nil {
				t.Fatal(err)
			}
			defer db.Close() // nolint:errcheck
			if err := db.Ping(); err != nil {
				t.Fatal(err)
			}
			// Test using db
		})

}
Output:

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// DefaultPullTimeout is the default timeout used when pulling images
	DefaultPullTimeout = time.Minute
	// DefaultTimeout is the default timeout used when starting a container and checking if it's ready
	DefaultTimeout = time.Minute
	// DefaultReadyTimeout is the default timeout used for each container ready check.
	// e.g. each invocation of the ReadyFunc
	DefaultReadyTimeout = 2 * time.Second
	// DefaultCleanupTimeout is the default timeout used when stopping and removing a container
	DefaultCleanupTimeout = 15 * time.Second
)

Functions

func Run

func Run(t *testing.T, imgName string, opts Options, testFunc func(*testing.T, ContainerInfo))

Run runs the given test function once the specified Docker image is running in a container

Types

type ContainerInfo

type ContainerInfo struct {
	ID        string
	Name      string
	ImageName string
	Ports     nat.PortMap
}

ContainerInfo holds information about a running Docker container

func (ContainerInfo) FirstPort

func (c ContainerInfo) FirstPort() (hostIP string, hostPort string, err error)

FirstPort gets the first published/bound/mapped TCP port. It is always safer to use Port(). This provided as a convenience method and should only be used with Docker images that only expose a single port. If the Docker image exposes multiple ports, then the "first" port will not always be the same.

func (ContainerInfo) FirstUDPPort

func (c ContainerInfo) FirstUDPPort() (hostIP string, hostPort string, err error)

FirstUDPPort gets the first published/bound/mapped UDP port. It is always safer to use UDPPort(). This provided as a convenience method and should only be used with Docker images that only expose a single port. If the Docker image exposes multiple ports, then the "first" port will not always be the same.

func (ContainerInfo) Port

func (c ContainerInfo) Port(containerPort uint16) (hostIP string, hostPort string, err error)

Port gets the specified published/bound/mapped TCP port

func (ContainerInfo) String

func (c ContainerInfo) String() string

String gets the string representation for the ContainerInfo. This is intended for debugging purposes.

func (ContainerInfo) UDPPort

func (c ContainerInfo) UDPPort(containerPort uint16) (hostIP string, hostPort string, err error)

UDPPort gets the specified published/bound/mapped UDP port

type Options

type Options struct {
	// PullTimeout is the timeout used when pulling images
	PullTimeout time.Duration
	// Timeout is the timeout used when starting a container and checking if it's ready
	Timeout time.Duration
	// ReadyTimeout is the timeout used for each container ready check.
	// e.g. each invocation of the ReadyFunc
	ReadyTimeout time.Duration
	// CleanupTimeout is the timeout used when stopping and removing a container
	CleanupTimeout time.Duration
	ReadyFunc      func(context.Context, ContainerInfo) bool
	Env            map[string]string
	Entrypoint     []string
	Cmd            []string
	// If you prefer to specify your port bindings as a string, use nat.ParsePortSpecs()
	PortBindings nat.PortMap
	PortRequired bool
	LogStdout    bool
	LogStderr    bool
	ShmSize      int64
}

Options contains the configurable options for running tests in the docker image

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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