gnomock

package module
v0.4.1 Latest Latest
Warning

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

Go to latest
Published: May 8, 2020 License: MIT Imports: 14 Imported by: 31

README

Gnomock – test your code 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

Build Go Report Card

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.

Documentation

Using Gnomock in Go applications

See package reference.

For Preset documentation, refer to Presets section.

Using Gnomock in other languages

Gnomock can be used from any language using its HTTP wrapper – gnomockd. It runs locally and receives commands via HTTP from anywhere. Communication with gnomockd is documented using OpenAPI 3.0, making integration easy for almost any language:

Preset usage

Presets allow to easily setup a service that is popular enough to have a public Gnomock implementation. Below is a sample Redis setup:

p := mockredis.Preset()
container, _ := gnomock.Start(p)

defer func() { _ = gnomock.Stop(container) }()

addr := container.DefaultAddress()
client := redis.NewClient(&redis.Options{Addr: addr})

With Gnomock it is easy to setup complex environments using multiple presets. It could be done in parallel. For example, below is a dependency stack of a Go program that uses Postgres database, Redis cache, AWS S3 storage and AWS SES:

containers, err := gnomock.InParallel().
    Start(mockredis.Preset()).
    Start(mockpostgres.Preset(), mockpostgres.WithUser("user", "pass")).
    Start(
            localstack.Preset(),
            localstack.WithServices(localstack.S3, localstack.SES),
         ).
    Go()

defer func() { _ = gnomock.Stop(containers...) }()

Official presets

The power of Gnomock is in the Presets developed by the community. Presets, both existing and planned, are listed below:

Preset Repository Reference
Localstack https://github.com/orlangure/gnomock-localstack Reference
Splunk https://github.com/orlangure/gnomock-splunk Reference
Redis https://github.com/orlangure/gnomock-redis Reference
MySQL https://github.com/orlangure/gnomock-mysql Reference
PostgreSQL https://github.com/orlangure/gnomock-postgres Reference
Microsoft SQL Server https://github.com/orlangure/gnomock-mssql Reference
MongoDB https://github.com/orlangure/gnomock-mongo Reference
Elasticsearch
DynamoDB
Cassandra
MariaDB

Usage without presets

Gnomock can be used directly, without Presets. It requires a bit more work since every use case needs its own healthcheck and initialization implementation, as well as detailed configuration targeted at that particular use case:

// assuming the container exposes 2 ports
namedPorts := gnomock.NamedPorts{
    "web80":   gnomock.TCP(80),
    "web8080": gnomock.TCP(8080),
}

// see docs for option description
container, err := gnomock.StartCustom(
    testImage, namedPorts,
    gnomock.WithHealthCheckInterval(time.Microsecond*500),
    gnomock.WithHealthCheck(healthcheck),
    gnomock.WithInit(initf),
    gnomock.WithContext(context.Background()),
    gnomock.WithStartTimeout(time.Second*30),
    gnomock.WithWaitTimeout(time.Second*1),
    gnomock.WithEnv("GNOMOCK_TEST_1=foo"),
    gnomock.WithEnv("GNOMOCK_TEST_2=bar"),
)

// stop and remove the container after tests
defer gnomock.Stop(container)

// ports bound on host are different from the ports containers expose
addr80 := container.Address("web80")
addr8080 := container.Address("web8080")

To keep test code clean and simple, it is better to wrap custom use cases with Preset implementation, that can be contributed back to the community.

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.

It can be used either directly, or via already existing implementations of various connectors built by the community.

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

Types

type Container

type Container struct {
	// Container ID as set by docker
	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

func StartCustom added in v0.3.0

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 added in v0.3.0

func (c *Container) DefaultAddress() string

DefaultAddress return Address() with DefaultPort

func (*Container) DefaultPort added in v0.3.0

func (c *Container) DefaultPort() int

DefaultPort returns Port() with DefaultPort

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(*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(*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 added in v0.1.0

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 added in v0.1.0

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 added in v0.1.0

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 added in v0.1.0

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 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 WithEnv added in v0.1.1

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 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 added in v0.2.1

func WithLogWriter(w io.Writer) Option

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

func WithOptions added in v0.4.0

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 WithStartTimeout

func WithStartTimeout(t time.Duration) Option

WithStartTimeout sets the amount of time to wait for a new Gnomock container to start. This includes pulling an image and creating a new container from it. To set the amount of time to wait before a created container healthy and ready to use, use WithWaitTimeout

func WithTag added in v0.3.0

func WithTag(tag string) Option

WithTag overrides docker image tag provided by a preset

func WithWaitTimeout

func WithWaitTimeout(t time.Duration) Option

WithWaitTimeout sets the amount of time to wait for a created container to become ready to use. If health check function does not return nil error until this timeout is reached, Start() fails

type Options added in v0.4.0

type Options struct {
	// StartTimeout is an amount of nanoseconds to wait for the container to be
	// created. This includes the time to pull docker image, create and start
	// the container. It does not include the time to wait for the container to
	// become healthy
	StartTimeout time.Duration `json:"start_timeout"`

	// WaitTimeout is an amount of nanoseconds to wait for a created container
	// to become healthy and ready to use
	WaitTimeout time.Duration `json:"wait_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"`

	// Tag sets docker image version to be used in this container. By default,
	// latest tag is used
	Tag string `json:"tag"`
	// 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 added in v0.3.0

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

Parallel is a builder object that configures parallel preset execution

func InParallel added in v0.3.0

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 added in v0.3.0

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 added in v0.3.0

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 added in v0.1.0

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

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

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

func TCP added in v0.1.0

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
cleaner Module

Jump to

Keyboard shortcuts

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