gnomock

package module
Version: v0.13.2 Latest Latest
Warning

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

Go to latest
Published: Apr 4, 2021 License: MIT Imports: 22 Imported by: 0

README

The fork of https://github.com/orlangure/gnomock with updated docker/docker and other updated dependencies.

All CI tests in this fork works the same as in the original repo.

Gnomock – tests without mocks

🏗️ Spin up entire dependency stack

🎁 Setup initial dependency state – easily!

🏭 Test against actual, close to production software

⏳ Spend no time writing mocks

🕹️ Test actual program behavior and side effects

PkgGoDev Test Go Report Card codecov

Gnomock is an integration and end-to-end testing toolkit. It uses Docker to create temporary containers for application dependencies, setup their initial state and clean them up in the end. Gnomock allows to test the code with no mocks wherever possible.

The power of Gnomock is in a variety of Presets, each implementing a specific database, service or other tools. Each preset provides ways of setting up its initial state as easily as possible: SQL schema creation, test data upload into S3, sending test events to Splunk, etc.

The name "Gnomock" stands for "no mock", with a "G" for "Go" 😼. It also sounds like "gnome", that's why the friendly garden gnome artwork (by Michael Zolotov)

Demo

See for yourself how easy and fast it is to write tests that use actual services running in ephemeral Docker containers:

asciicast

Table of contents

Getting started

Gnomock can be used in two different ways:

  • Imported directly as a package in any Go project
  • Accessed over HTTP running as a daemon in any other language

⚠️ Both ways require an active Docker daemon running locally in the same environment.

Using Gnomock in Go applications

See the following example to get started:

go get github.com/etecs-ru/gnomock

Setting up a Postgres container with schema setup example:

import (
"database/sql"

_ "github.com/lib/pq" // postgres driver
"github.com/etecs-ru/gnomock"
"github.com/etecs-ru/gnomock/preset/postgres"
)

p := postgres.Preset(
postgres.WithUser("gnomock", "gnomick"),
postgres.WithDatabase("mydb"),
postgres.WithQueriesFile("/var/project/db/schema.sql"),
)
container, _ := gnomock.Start(p)
defer func () { _ = gnomock.Stop(container) }()

connStr := fmt.Sprintf(
"host=%s port=%d user=%s password=%s  dbname=%s sslmode=disable",
container.Host, container.DefaultPort(),
"gnomock", "gnomick", "mydb",
)
db, _ := sql.Open("postgres", connStr)
// db has the required schema and data, and is ready to use

See package reference. For Preset documentation, refer to Presets section.

Using Gnomock in other languages

If you use Go, please refer to Using Gnomock in Go applications section. Otherwise, refer to documentation.

Official presets

The power of Gnomock is in the Presets. Existing Presets with their supported* versions are listed below.

* Supported versions are tested as part of CI pipeline. Other versions might work as well.

Preset Go package HTTP API Go API Supported versions
Localstack (AWS) Go package Docs Reference 0.12.2
Splunk Go package Docs Reference 8.0.2
Redis Go package Docs Reference 5.0.10, 6.0.9
Memcached Go package Docs Reference 1.6.9
MySQL Go package Docs Reference 5.7.32, 8.0.22
MariaDB Go package Docs Reference 10.5.8
PostgreSQL Go package Docs Reference 10.15, 11.10, 12.5, 13.1
Microsoft SQL Server Go package Docs Reference 2017-latest, 2019-latest
MongoDB Go package Docs Reference 3.6.21, 4.4
RabbitMQ Go package Docs Reference 3.8.9-alpine, 3.8.9-management-alpine
Kafka Go package Docs Reference 2.5.1-L0
Elasticsearch Go package Docs Reference 5.6, 6.8.13, 7.9.3
Kubernetes Go package Docs Reference v1.19.3
CockroachDB Go package Docs Reference v19.2.11, v20.1.10

It is possible to use Gnomock directly from Go code without any presets. HTTP API only allows to setup containers using presets that exist in this repository.

Similar projects

Gnomock is not the only project that aims to simplify integration and end-to-end testing by using ephemeral docker containers:

  • testcontainers/testcontainers-go
  • ory/dockertest

These projects are amazing, and they give plenty of flexibility and power to their users. There are many things that are possible with them, but are impossible with Gnomock. Still, below is a short list of things that sometimes give Gnomock an advantage:

  • Gnomock tries to provide a batteries-included solution. Gnomock has a growing number of Presets, each one implementing an integration with a popular external service. For every Preset, there already is a number of "invisible" utilities that transparently relieve you from implementing them yourself:
    • Built-in health check function that you don't even need to know it exists. It makes sure you only get control over a container when it is ready to use.
    • Wrappers for some of the configuration exposed by the container, such as default username/password. You can easily provide your own credentials to connect to the container.
    • Seed data ingestion for your convenience. Sometimes you just need to make sure your queries work given some data. Gnomock puts your data in there with a single line of code. Sometimes you only test a program that consumes messages from Kafka, and Gnomock produces the messages for you with another line of code.
  • Simple API that does not expose anything that happens "under the hood" most of the time. Yet Gnomock allows some additional configuration and custom Preset implementation whenever necessary.
  • Gnomock's vision includes being useful not only in Go projects, but in any projects via HTTP. It already supports almost all its features over HTTP layer, has a clear OpenAPI spec, and even a proof of concept wrapper in Python.
  • Gnomock has a friendly garden gnome mascot😻

Troubleshooting

Tests with Gnomock take too long and time-out eventually

It happens a lot locally if your internet isn't fast enough to pull docker images used in tests. In CI, such as in Github Actions, the images are downloaded very quickly. To work around this issue locally, pull the image manually before running the tests. You only need to do it once, the images stay in local cache until deleted. For example, to pull Postgres 11 image, run:

docker pull postgres:11

Tests time-out even when the image exists locally

It can happen if the containers can't become ready to use before they time out. By default, Gnomock uses fairly high timeouts for new containers (for starting and for setting them up). If you choose to change default timeout using WithTimeout (timeout in HTTP), it is possible that the values you choose are too short.

Tests pass when run one-by-one, and fail when run in parallel

It happens when you try to start up a lot of containers at the same time. The system, especially in CI environments such as Github Actions, cannot handle the load, and containers fail to become healthy before they time-out. That's the reason Gnomock has a few separate build jobs, each running only a small subset of tests, one package at a time.

Containers fail to setup with a "File not found" error

If you run gnomock as a server, you need to make sure the files you use in your setup are available inside gnomock container. Use -v $(pwd):$(pwd) argument to docker run to mount the current working directory under the same path inside the gnomock container. If you prefer to keep a permanent gnomock container running, you can mount your entire $HOME directory (or any other directory where you keep the code).

Documentation

Overview

Package gnomock contains a framework to set up temporary docker containers for integration and end-to-end testing of other applications. It handles pulling images, starting containers, waiting for them to become available, setting up their initial state and cleaning up in the end.

Its power is in a variety of Presets, each implementing a specific database, service or other tools. Each preset provides ways of setting up its initial state as easily as possible: SQL schema creation, test data upload into S3, sending test events to Splunk, etc.

All containers created using Gnomock have a self-destruct mechanism that kicks-in right after the test execution completes.

To debug cases where containers don't behave as expected, there are options like `WithDebugMode()` or `WithLogWriter()`.

Index

Constants

View Source
const DefaultPort = "default"

DefaultPort should be used with simple containers that expose only one TCP port. Use DefaultTCP function when creating a container, and use DefaultPort when calling Address()

Variables

View Source
var ErrEnvClient = fmt.Errorf("can't connect to docker host")

ErrEnvClient means that Gnomock can't connect to docker daemon in the testing environment. See https://docs.docker.com/compose/reference/overview/ for information on required configuration

View Source
var ErrPortNotFound = errors.New("port not found")

ErrPortNotFound means that a port with the requested name was not found in the created container. Make sure that the name used in Find method matches the name used in in NamedPorts. For default values, use DefaultTCP and DefaultPort

Functions

func Stop

func Stop(cs ...*Container) error

Stop stops the provided container and lets docker remove them from the system. Stop returns an error if any one of the containers couldn't stop. If these containers have sidecar containers, they are stopped as well.

Types

type Container

type Container struct {
	// A unique identifier of this container. The format of this ID may change
	// in the future.
	ID string `json:"id,omitempty"`

	// Host name of bound ports
	//
	// Default: localhost
	Host string `json:"host,omitempty"`

	// A collections of ports exposed by this container. Each port has an alias
	// and an actual port number as exposed on the host
	Ports NamedPorts `json:"ports,omitempty"`
	// contains filtered or unexported fields
}

Container represents a docker container created for testing. Host and Ports fields should be used to configure the connection to this container. ID matches the original docker container ID

func Start

func Start(p Preset, opts ...Option) (*Container, error)

Start creates a container using the provided Preset. The Preset provides its own Options to configure Gnomock container. Usually this is enough, but it is still possible to extend/override Preset options with new values. For example, wait timeout defined in the Preset, if at all, might be not enough for this particular usage, so it can't be changed during this call.

All provided Options are applied. First, Preset options are applied. Then, custom Options. If both Preset and custom Options change the same configuration, custom Options are used.

It is recommended, but not required, to call `gnomock.Stop()` when the tests complete to cleanup the containers.

func StartCustom

func StartCustom(image string, ports NamedPorts, opts ...Option) (c *Container, err error)

StartCustom creates a new container using provided image and binds random ports on the host to the provided ports inside the container. Image may include tag, which is set to "latest" by default. Optional configuration is available through Option functions. The returned container must be stopped when no longer needed using its Stop() method

func (*Container) Address

func (c *Container) Address(name string) string

Address is a convenience function that returns host:port that can be used to connect to this container. If a container was created with DefaultTCP call, use DefaultPort as the name. Otherwise, use the name of one of the ports used during setup

func (*Container) DefaultAddress

func (c *Container) DefaultAddress() string

DefaultAddress return Address() with DefaultPort

func (*Container) DefaultPort

func (c *Container) DefaultPort() int

DefaultPort returns Port() with DefaultPort

func (*Container) DockerID

func (c *Container) DockerID() string

DockerID returns the ID of this container as known to Docker.

func (*Container) Port

func (c *Container) Port(name string) int

Port is a convenience function that returns port number with the provided name

type HealthcheckFunc

type HealthcheckFunc func(context.Context, *Container) error

HealthcheckFunc defines a function to be used to determine container health. It receives a host and a port, and returns an error if the container is not ready, or nil when the container can be used. One example of HealthcheckFunc would be an attempt to establish the same connection to the container that the application under test uses

type InitFunc

type InitFunc func(context.Context, *Container) error

InitFunc defines a function to be called on a ready to use container to set up its initial state before running the tests. For example, InitFunc can take care of creating a SQL table and inserting test data into it

type NamedPorts

type NamedPorts map[string]Port

NamedPorts is a collection of ports exposed by a container, where every exposed port is given a name. Some examples of names are "web" or "api" for a container that exposes two separate ports: one for web access and another for API calls

func DefaultTCP

func DefaultTCP(port int) NamedPorts

DefaultTCP is a utility function to use with simple containers exposing a single TCP port. Use it to create default named port for the provided port number. Pass DefaultPort to Address() method to get the address of the default port

func (NamedPorts) Find

func (p NamedPorts) Find(proto string, portNum int) (string, error)

Find returns the name of a port with the provided protocol and number. Use this method to find out the name of an exposed ports, when port number and protocol are known

func (NamedPorts) Get

func (p NamedPorts) Get(name string) Port

Get returns a port with the provided name. An empty value is returned if there are no ports with the given name

type Option

type Option func(*Options)

Option is an optional Gnomock configuration. Functions implementing this signature may be combined to configure Gnomock containers for different use cases

func WithCommand

func WithCommand(cmd string, args ...string) Option

WithCommand sets the command and its arguments to execute when container first runs. This command replaces the command defined in docker image.

func WithContainerName

func WithContainerName(name string) Option

WithContainerName allows to give a specific name to a new container. If a container with the same name already exists, it is killed.

func WithContext

func WithContext(ctx context.Context) Option

WithContext sets the provided context to be used for setting up a Gnomock container. Canceling this context will cause Start() to abort

func WithDebugMode

func WithDebugMode() Option

WithDebugMode allows Gnomock to output internal messages for debug purposes. Containers created in debug mode will not be automatically removed on failure to setup their initial state. Containers still might be removed if they are shut down from the inside. Use WithLogWriter to see what happens inside.

func WithDisableAutoCleanup

func WithDisableAutoCleanup() Option

WithDisableAutoCleanup disables auto-removal of this container when the tests complete. Automatic cleanup is a safety net for tests that for some reason fail to run `gnomock.Stop()` in the end, for example due to an unexpected `os.Exit()` somewhere.

func WithEnv

func WithEnv(env string) Option

WithEnv adds environment variable to the container. For example, AWS_ACCESS_KEY_ID=FOOBARBAZ

func WithHealthCheck

func WithHealthCheck(f HealthcheckFunc) Option

WithHealthCheck allows to define a rule to consider a Gnomock container ready to use. For example, it can attempt to connect to this container, and return an error on any failure, or nil on success. This function is called repeatedly until the timeout is reached, or until a nil error is returned

func WithHealthCheckInterval

func WithHealthCheckInterval(t time.Duration) Option

WithHealthCheckInterval defines an interval between two consecutive health check calls. This is a constant interval

func WithHostMounts

func WithHostMounts(src, dst string) Option

WithHostMounts allows to bind host path (`src`) inside the container under `dst` path.

func WithInit

func WithInit(f InitFunc) Option

WithInit lets the provided InitFunc to be called when a Gnomock container is created, but before Start() returns. Use this function to run arbitrary code on the new container before using it. It can be useful to bring the container to a certain state (e.g create SQL schema)

func WithLogWriter

func WithLogWriter(w io.Writer) Option

WithLogWriter sets the target where to write container logs. This can be useful for debugging

func WithOptions

func WithOptions(options *Options) Option

WithOptions allows to provide an existing set of Options instead of using optional configuration.

This way has its own limitations. For example, context or initialization functions cannot be set in this way

func WithPrivileged

func WithPrivileged() Option

WithPrivileged starts a container in privileged mode (like `docker run --privileged`). This option should not be used unless you really need it. One use case for this option would be to run a Preset that has some kind of docker-in-docker functionality.

func WithTimeout

func WithTimeout(t time.Duration) Option

WithTimeout sets the amount of time to wait for a created container to become ready to use. All startup steps must complete before they time out: start, wait until healthy, init.

type Options

type Options struct {
	// Timeout is an amount of time to wait before considering Start operation
	// as failed.
	Timeout time.Duration `json:"timeout"`

	// Env is a list of environment variable to inject into the container. Each
	// entry is in format ENV_VAR_NAME=value
	Env []string `json:"env"`

	// Debug flag allows Gnomock to be verbose about steps it takes
	Debug bool `json:"debug"`

	// Privileged starts a container in privileged mode.
	Privileged bool `json:"privileged"`

	// ContainerName allows to use a specific name for a new container. In case
	// a container with the same name already exists, Gnomock kills it.
	ContainerName string `json:"container_name"`

	// Cmd is an optional command with its arguments to execute on container
	// startup. This command replaces the default one set on docker image
	// level.
	Cmd []string `json:"cmd"`

	// HostMounts allows to mount local paths into the container.
	HostMounts map[string]string `json:"host_mounts"`

	// DisableAutoCleanup prevents the container from being automatically
	// stopped and removed after the tests are complete. By default, Gnomock
	// will try to stop containers created by it right after the tests exit.
	DisableAutoCleanup bool `json:"disable_cleanup"`
	// contains filtered or unexported fields
}

Options includes Gnomock startup configuration. Functional options (WithSomething) should be used instead of directly initializing objects of this type whenever possible

type Parallel

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

Parallel is a builder object that configures parallel preset execution

func InParallel

func InParallel() *Parallel

InParallel begins parallel preset execution setup. Use Start to add more presets with their configuration to parallel execution, and Go() in the end to kick-off everything

func (*Parallel) Go

func (b *Parallel) Go() ([]*Container, error)

Go kicks-off parallel preset execution. Returned containers are in the same order as they were added with Start. An error is returned if any of the containers failed to start and become available. Even if Go() returns an errors, there still might be containers created in the process, and it is callers responsibility to Stop them

func (*Parallel) Start

func (b *Parallel) Start(p Preset, opts ...Option) *Parallel

Start adds the provided preset with its configuration to the parallel execution kicked-off by Go(), together with other added presets

type Port

type Port struct {
	// Protocol of the exposed port (TCP/UDP).
	Protocol string `json:"protocol"`

	// Port number of the exposed port.
	Port int `json:"port"`

	// HostPort is an optional value to set mapped host port explicitly.
	HostPort int `json:"host_port"`
}

Port is a combination of port number and protocol that are exposed in a container

func TCP

func TCP(port int) Port

TCP returns a Port with the provided number and "tcp" protocol. This is a utility function, it is equivalent to creating a Port explicitly

type Preset

type Preset interface {
	// Image returns a canonical docker image used to setup this Preset.
	Image() string

	// Ports returns a group of ports exposed by this Preset, where every port
	// is given a unique name. For example, if a container exposes API endpoint
	// on port 8080, and web interface on port 80, there should be two named
	// ports: "web" and "api".
	Ports() NamedPorts

	// Options returns a list of Option functions that allow to setup this
	// Preset implementation.
	Options() []Option
}

Preset is a type that includes ready to use Gnomock configuration. Such configuration includes image and ports as well as options specific to this implementation. For example, well known services like Redis or Postgres may have Gnomock implementations, providing healthcheck functions and basic initialization options.

Directories

Path Synopsis
cmd
preset
cockroachdb
Package cockroachdb includes CockroachDB implementation of Gnomock Preset interface.
Package cockroachdb includes CockroachDB implementation of Gnomock Preset interface.
elastic
Package elastic provides a Gnomock Preset for Elasticsearch.
Package elastic provides a Gnomock Preset for Elasticsearch.
k3s
Package k3s provides a Gnomock Preset for lightweight kubernetes (k3s).
Package k3s provides a Gnomock Preset for lightweight kubernetes (k3s).
kafka
Package kafka provides a Gnomock Preset for Kafka.
Package kafka provides a Gnomock Preset for Kafka.
localstack
Package localstack provides a Gnomock Preset for localstack project (https://github.com/localstack/localstack).
Package localstack provides a Gnomock Preset for localstack project (https://github.com/localstack/localstack).
mariadb
Package mariadb provides a Gnomock Preset for MariaDB database
Package mariadb provides a Gnomock Preset for MariaDB database
memcached
Package memcached includes Memcached implementation of Gnomock Preset interface.
Package memcached includes Memcached implementation of Gnomock Preset interface.
mongo
Package mongo includes mongo implementation of Gnomock Preset interface.
Package mongo includes mongo implementation of Gnomock Preset interface.
mssql
Package mssql provides a Gnomock Preset for Microsoft SQL Server database
Package mssql provides a Gnomock Preset for Microsoft SQL Server database
mysql
Package mysql provides a Gnomock Preset for MySQL database
Package mysql provides a Gnomock Preset for MySQL database
postgres
Package postgres provides a Gnomock Preset for PostgreSQL database.
Package postgres provides a Gnomock Preset for PostgreSQL database.
rabbitmq
Package rabbitmq provides a Gnomock Preset for RabbitMQ.
Package rabbitmq provides a Gnomock Preset for RabbitMQ.
redis
Package redis includes Redis implementation of Gnomock Preset interface.
Package redis includes Redis implementation of Gnomock Preset interface.
splunk
Package splunk includes Splunk Enterprise implementation of Gnomock Preset interface.
Package splunk includes Splunk Enterprise implementation of Gnomock Preset interface.
internal
cleaner
Package cleaner exposes an API to communicate with Gnomock Cleaner, a separate process that waits for incoming connections and terminates running containers when these connections are closed.
Package cleaner exposes an API to communicate with Gnomock Cleaner, a separate process that waits for incoming connections and terminates running containers when these connections are closed.
errors
Package errors exposes errors types used by gnomockd endpoints.
Package errors exposes errors types used by gnomockd endpoints.
gnomockd
Package gnomockd is an HTTP wrapper around Gnomock
Package gnomockd is an HTTP wrapper around Gnomock
health
Package health includes common health check functions to use in other packages.
Package health includes common health check functions to use in other packages.
israce
Package israce reports if the Go race detector is enabled.
Package israce reports if the Go race detector is enabled.
registry
Package registry provides access to existing presets.
Package registry provides access to existing presets.
testutil
Package testutil includes utilities used in test code of other packages.
Package testutil includes utilities used in test code of other packages.

Jump to

Keyboard shortcuts

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