conex

package module
v0.10.0 Latest Latest
Warning

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

Go to latest
Published: Jun 16, 2026 License: MIT Imports: 27 Imported by: 0

README

Conex GoDoc Go Report Card

Conex integrates Go testing with Docker (and Tart, experimentally) so integration tests can start real dependencies with less boilerplate.

Why?

Integration tests are high-value when they run against real services. Conex handles common setup work so tests stay focused on behavior:

  • Start and stop containers
  • Create unique names to avoid collisions
  • Pull images (or build from Dockerfiles) before tests run
  • Wait for TCP/UDP ports to accept connections
  • Expose ports

It also supports a driver convention so reusable test helpers can register their required images.

Quick Start

To use conex, simply call conex.Main(m) from TestMain:

package example_test

import (
  "testing"

  "github.com/omeid/conex"
)

func TestMain(m *testing.M) {
  conex.Main(m)
}

Example

package example_test

import (
  "testing"

  "github.com/omeid/conex"
  "github.com/conex/postgresql"
)

func TestMain(m *testing.M) {
  conex.Main(m)
}

func TestPostgreSQL(t *testing.T) {
  db, container := postgresql.Box(t)

 _ = db
  // use db to interact with the postgresql database

  // you can also execute commands directly inside the container
  // using an API that closely matches os/exec:
  // cmd := container.Exec("psql", "-U", "postgres", "-c", "CREATE DATABASE testdb;")
  // out, err := cmd.CombinedOutput()
}

Advanced Container Options

Config supports Docker-specific container options:

c := conex.Box(t, &conex.Config{
  Image:      "docker:dind",
  Privileged: true,
  Binds:      []string{"/var/run/docker.sock:/var/run/docker.sock"},
})

Privileged and Binds are Docker runner options only.

Driver Packages

Conex drivers are small packages that wrap a service container with a native client API. This lets tests focus on the service instead of raw container lifecycle details.

A driver usually:

  1. Defines an Image variable
  2. Registers it with conex.Require(...)
  3. Exposes a helper that returns both a client and a conex.Container

See the echo box source for a concrete example.

Available boxes from github.com/conex/*:

Image References

An image can be either:

  • A registry reference: name[:tag|@digest]
  • A Dockerfile path: Dockerfile or Dockerfile.suffix

Before tests run, Conex either pulls/builds these images or validates they already exist, based on configuration (conex.OptPullImages and conex.OptBuildImages).

Runners

Conex auto-detects the runner unless specified via conex.OptRunnerType:

  • Linux + local Docker socket: native runner (direct container IP)
  • macOS/Windows/remote Docker: docker runner (tests run in a container)
Native Runner

Runs tests on the host and connects directly to container IPs.

Docker Runner

Runs tests inside a container on a shared conex network. This avoids host-network limitations on Docker Desktop and remote Docker hosts.

When using the docker runner, Conex:

  1. Creates a conex network
  2. Runs the test binary in a Go container on that network
  3. Starts service containers on the same network
  4. Lets containers communicate via container names

Customize the Go image used by the docker runner:

func TestMain(m *testing.M) {
  conex.Main(
    m,
    conex.OptGoImage("golang:1.21-alpine"),
  )
}
Tart Runner (Experimental)

The Tart runner creates macOS/Linux VMs using Tart on Apple Silicon Macs.

CONEX_RUNNER=tart go test ./...

Tart image references should be Tart VM images (for example, ghcr.io/cirruslabs/macos-sequoia-base:latest). Dockerfile image refs are not supported with the Tart runner.

Overriding Auto-Detection

While Conex auto-detects the runner by default, you can explicitly override it using an environment variable:

# Force native runner
CONEX_RUNNER=native go test ./...

# Force docker runner
CONEX_RUNNER=docker go test ./...

Alternatively, you can specify the runner programmatically in your tests using conex.OptRunnerType:

func TestMain(m *testing.M) {
  conex.Main(
    m,
    conex.OptRunnerType(conex.RunnerDocker), // Explicitly force the Docker runner
  )
}

Configuration

You can configure Conex per run:

func TestMain(m *testing.M) {
  conex.Main(
    m,
    conex.OptPullImages(true),
    conex.OptBuildImages(true),
    conex.OptGoImage("golang:1.22"),
    conex.OptReturnCode(255),
  )
}

License

MIT

Documentation

Overview

Package conex provides easy to use Docker Integration with Testing.

Index

Constants

View Source
const (
	// ConexNetworkName is the name of the Docker network used for conex containers.
	ConexNetworkName = "conex"
	// ConexRunnerEnv is the environment variable that indicates we're running inside a conex container.
	ConexRunnerEnv = "CONEX_INSIDE_DOCKER"
)

Variables

View Source
var (
	// FailReturnCode is used as status code when conex fails to setup during Run.
	// This does not override the return value of testing.M.Run, only when conex
	// fails to even testing.M.Run.
	//
	// Deprecated: Use OptReturnCode instead.
	FailReturnCode = 255

	// PullImages dictates whether the Manager should attempt to pull images
	// on run or simply ensure they exist.
	//
	// Deprecated: Use OptPullImages instead.
	PullImages = true

	// BuildImages dictates whether the Manager should attempt to build images
	// on run or simply ensure they exist.
	//
	// Deprecated: Use OptBuildImages instead.
	BuildImages = true

	// GoImage is the Docker image used to run tests inside a container when
	// using the Docker runner. This should be a Go image that matches your
	// Go version. Set this before calling Run() if you need a specific version.
	// Example: "golang:1.21-alpine"
	//
	// Deprecated: Use OptGoImage instead.
	GoImage = "golang:1.22"
)
View Source
var ErrPortWaitTimedOut = errors.New("wait timeout")

ErrPortWaitTimedOut is returned when Container.Wait reaches maxWait before the port accepts connections.

Functions

func Logf added in v0.1.1

func Logf(t testing.TB, plugin string, f string, args ...any)

Logf logs directly to stdout to avoid the test file and line number prefix, providing a cleaner output format. It can be used by plugins to log uniformly.

func Main added in v0.0.7

func Main(m *testing.M, opts ...Option)

Main is a helper that wraps Run in os.Exit, intended to be called from TestMain.

func Require

func Require(images ...func() string)

Require adds the image name returned by the provided functions to the list of images pulled by the default Manager when Run is called. Used by driver packages, see conex/redis, conex/rethink.

func Run

func Run(m *testing.M, opts ...Option) int

Run prepares a docker client, pulls the provided list of images and then runs your tests.

Types

type Cmd added in v0.0.6

type Cmd struct {
	// Path is the path of the command to run.
	Path string

	// Args holds command line arguments, including the command as Args[0].
	Args []string

	// Env specifies the environment of the process.
	Env []string

	// Dir specifies the working directory of the command.
	Dir string

	// Stdin specifies the process's standard input.
	Stdin io.Reader

	// Stdout and Stderr specify the process's standard output and error.
	Stdout io.Writer
	Stderr io.Writer
	// contains filtered or unexported fields
}

Cmd represents an external command being prepared or run. It has similar fields and methods to os/exec.Cmd.

func (*Cmd) CombinedOutput added in v0.0.6

func (c *Cmd) CombinedOutput() ([]byte, error)

CombinedOutput runs the command and returns its combined standard output and standard error.

func (*Cmd) Output added in v0.0.6

func (c *Cmd) Output() ([]byte, error)

Output runs the command and returns its standard output.

func (*Cmd) Run added in v0.0.6

func (c *Cmd) Run() error

Run starts the specified command and waits for it to complete.

func (*Cmd) Start added in v0.0.6

func (c *Cmd) Start() error

Start starts the specified command but does not wait for it to complete.

func (*Cmd) Wait added in v0.0.6

func (c *Cmd) Wait() error

Wait waits for the command to exit and waits for any copying to stdin or copying from stdout or stderr to complete.

type Config

type Config struct {
	Image      string   // Name of the image as it was passed by the operator (e.g. could be symbolic)
	Env        []string // List of environment variable to set in the container
	Entrypoint []string // Entrypoint to run in the container
	Cmd        []string // Command to run when starting the container
	Hostname   string   // Hostname
	Domainname string   // Domainname
	User       string   // User that will run the command(s) inside the container, also support user:group
	Expose     []string // Ports to expose, supports the docker command line style syntax proto/port or just port which defaults to tcp
	Privileged bool     // Run the container in privileged mode
	Binds      []string // Volume binds (e.g. "/host/path:/container/path")
}

Config contains the configuration data about a container.

type Container

type Container interface {
	ID() string
	Name() string
	Image() string
	Address() string

	// Drop stops and removes the container. It is automatically called
	// via t.Cleanup, but can be called manually. Calling it multiple
	// times is a no-op.
	Drop()

	Wait(port string, timeout time.Duration) error // Wait for the port to respond to tcp/udp.

	// Exec creates a command to run inside the container.
	Exec(cmd ...string) *Cmd

	Logs(stdout io.Writer, stderr io.Writer) error
}

Container is a simple interface to a docker container.

func Box

func Box(t testing.TB, conf *Config) Container

Box creates a new container using the provided image and passes your parameters.

type DockerRunner added in v0.0.2

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

DockerRunner runs tests inside a Docker container on the same network as other conex containers. This allows conex to work on systems where container IPs are not directly accessible (e.g., Docker for Mac).

func NewDockerRunner added in v0.0.2

func NewDockerRunner(config *RunnerConfig) *DockerRunner

NewDockerRunner creates a new Docker runner.

func (*DockerRunner) Box added in v0.0.2

func (r *DockerRunner) Box(t testing.TB, conf *Config, name string) Container

Box creates a container on the conex network and returns a Container that uses the container name for connections.

func (*DockerRunner) Run added in v0.0.2

func (r *DockerRunner) Run(m *testing.M) int

Run executes the tests. If we're already inside a Docker container (detected by environment variable), it just runs the tests. Otherwise, it creates a container, mounts the current directory, and runs the tests inside it.

type Manager

type Manager interface {
	Run(m *testing.M, images ...string) int
	Box(t testing.TB, config *Config) Container
}

Manager is the conex container manager.

func New

func New(options ...Option) Manager

New creates a new conex manager with the given options. Options take precedence over package-level defaults.

type NativeRunner added in v0.0.2

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

NativeRunner runs tests on the host machine and connects to containers via their IP addresses. This requires native Docker (not Docker for Mac).

func NewNativeRunner added in v0.0.2

func NewNativeRunner(config *RunnerConfig) *NativeRunner

NewNativeRunner creates a new native runner.

func (*NativeRunner) Box added in v0.0.2

func (r *NativeRunner) Box(t testing.TB, conf *Config, name string) Container

Box creates a container and returns a Container that uses the container's direct IP address for connections.

func (*NativeRunner) Run added in v0.0.2

func (r *NativeRunner) Run(m *testing.M) int

Run executes the tests directly on the host.

type Option added in v0.0.4

type Option func(conf *managerConfig)

func OptBuildImages added in v0.0.4

func OptBuildImages(build bool) Option

func OptGoImage added in v0.0.4

func OptGoImage(image string) Option

func OptPullImages added in v0.0.4

func OptPullImages(pull bool) Option

func OptRequireImage added in v0.0.4

func OptRequireImage(image string) Option

func OptReturnCode added in v0.0.4

func OptReturnCode(code int) Option

func OptRunnerType added in v0.0.5

func OptRunnerType(runner RunnerType) Option

OptRunnerType allows setting the RunnerType explicitly.

type Runner added in v0.0.2

type Runner interface {
	// Run executes the test suite. The runner is responsible for setting up
	// any necessary environment and executing m.Run().
	Run(m *testing.M) int

	// Box creates a container and returns a Container interface.
	// The implementation determines how the container is accessed (direct IP vs network alias).
	Box(t testing.TB, conf *Config, name string) Container
}

Runner is an abstraction that allows running tests either natively on the host or inside a Docker container. This enables conex to work on systems where container IPs are not directly accessible from the host (e.g., Docker for Mac).

type RunnerConfig added in v0.0.2

type RunnerConfig struct {
	Client     client.APIClient
	Name       string // prefix for container names
	PullImages bool
	Images     []string
	RetCode    int
	Counter    *counter
	GoImage    string // Go image for running tests in Docker runner
}

RunnerConfig holds configuration for creating a runner.

type RunnerType added in v0.0.2

type RunnerType string

RunnerType specifies which runner implementation to use.

const (
	// RunnerNative runs tests on the host with direct container IP access.
	// This is the default and requires native Docker.
	RunnerNative RunnerType = "native"

	// RunnerDocker runs containers on a shared network, allowing tests to
	// work on systems where container IPs are not accessible from the host
	// (e.g., Docker for Mac, Docker Machine).
	RunnerDocker RunnerType = "docker"
)
const (
	// RunnerTart runs VMs using Tart virtualization.
	// Container IPs are directly accessible from the host.
	RunnerTart RunnerType = "tart"
)

type TartRunner added in v0.0.3

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

TartRunner runs tests on the host machine and manages Tart VMs as containers. VMs are cloned from base images and accessed via their direct IP addresses.

func NewTartRunner added in v0.0.3

func NewTartRunner(config *RunnerConfig) *TartRunner

NewTartRunner creates a new tart runner.

func (*TartRunner) Box added in v0.0.3

func (r *TartRunner) Box(t testing.TB, conf *Config, name string) Container

Box clones a Tart VM from the given image and starts it. The Config.Image field specifies the Tart VM image to clone from. Cmd, Env, and Expose are supported through tart exec after boot.

func (*TartRunner) Run added in v0.0.3

func (r *TartRunner) Run(m *testing.M) int

Run executes the tests directly on the host.

Jump to

Keyboard shortcuts

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