testkit

package
v1.11.0 Latest Latest
Warning

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

Go to latest
Published: Feb 14, 2026 License: MIT Imports: 10 Imported by: 0

README

testkit

testkit is a small helper package for integration tests that use testcontainers-go.

Features

  • NewSuite(t):
    • checks Docker availability,
    • provides a shared context,
    • auto-terminates containers on test cleanup.
  • RequireIntegration(t):
    • optional env guard (RUN_INTEGRATION_TESTS=1).
  • StartEMQX(opts):
    • starts EMQX with dashboard credentials and returns connection metadata.
  • StartPostgres(opts):
    • starts PostgreSQL and returns postgres:// URL via pg.URL().
  • StartRedis(opts):
    • starts Redis and returns host/port via rd.Addr().
  • StartMinIO(opts):
    • starts MinIO and returns API/console endpoints.

Usage

1) Direct usage in an integration test
//go:build integration

import (
	"testing"

	"github.com/bronystylecrazy/ultrastructure/testkit"
	"github.com/stretchr/testify/require"
)

func TestMyIntegration(t *testing.T) {
	require := require.New(t)

	testkit.RequireIntegration(t)

	suite := testkit.NewSuite(t)
	pg := suite.StartPostgres(testkit.PostgresOptions{})
	rd := suite.StartRedis(testkit.RedisOptions{})
	s3 := suite.StartMinIO(testkit.MinIOOptions{})

	require.NotEmpty(pg.URL())
	require.NotEmpty(rd.Addr())
	require.NotEmpty(s3.Endpoint)
}
2) Consumer repo pattern (di.App + Populate)
//go:build integration

import (
	"testing"

	"github.com/bronystylecrazy/ultrastructure/testkit"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"yourrepo/domain/order"
	"yourrepo/domain/payment"
)

func TestCheckout(t *testing.T) {
	require := require.New(t)
	assert := assert.New(t)

	testkit.RequireIntegration(t)

	var orderSvc *order.Service
	var paymentSvc *payment.Service

	NewSuiteEnv(t).
		Populate(&orderSvc, &paymentSvc).
		Start()

	require.NotNil(orderSvc)
	require.NotNil(paymentSvc)

	// perform assertions with orderSvc / paymentSvc
	assert.True(true)
}

Example SuiteEnv API shape:

  • NewSuiteEnv(t) starts required containers once for the test.
  • Populate(&svcA, &svcB) appends di.Populate(...) targets.
  • Start() builds and starts di.App(...), and registers cleanup.
// test/integration/testkit/suite_env.go
package testkit

import (
	"testing"

	"github.com/bronystylecrazy/ultrastructure/caching/rd"
	"github.com/bronystylecrazy/ultrastructure/database"
	"github.com/bronystylecrazy/ultrastructure/di"
	ustk "github.com/bronystylecrazy/ultrastructure/testkit"
	"go.uber.org/fx/fxtest"

	"yourrepo/app"
)

type SuiteEnv struct {
	t     *testing.T
	nodes []any
	app   *fxtest.App
}

func NewSuiteEnv(t *testing.T) *SuiteEnv {
	t.Helper()

	suite := ustk.NewSuite(t)
	pg := suite.StartPostgres(ustk.PostgresOptions{})
	redisCtr := suite.StartRedis(ustk.RedisOptions{})

	return &SuiteEnv{
		t: t,
		nodes: []any{
			app.Module(),
			di.Replace(database.Config{
				Dialect:    "postgres",
				Datasource: pg.URL(),
			}),
			di.Replace(rd.Config{
				Addr:     redisCtr.Addr(),
				Password: redisCtr.Password,
				Protocol: 3,
			}),
		},
	}
}

func (e *SuiteEnv) Populate(targets ...any) *SuiteEnv {
	e.t.Helper()
	for _, target := range targets {
		e.nodes = append(e.nodes, di.Populate(target))
	}
	return e
}

func (e *SuiteEnv) Start() *SuiteEnv {
	e.t.Helper()
	e.app = fxtest.New(e.t, di.App(e.nodes...).Build())
	e.app.RequireStart()
	e.t.Cleanup(e.app.RequireStop)
	return e
}
3) Scenario/case style (parallel-safe)
  • Use t.Run("scenario: ...") for business flows.
  • Use table-driven cases inside each scenario.
  • In each case: tc := tc, t.Parallel(), unique caseID.
  • Isolate with transaction rollback + unique Redis/S3 prefixes.

testify pattern recommendation:

  • use require.* for setup and critical preconditions.
  • use assert.* for post-action validations where you want multiple checks.

Suggested Project Structure (Consumer Repo)

yourrepo/
  app/
  domain/
    order/
      order_integration_test.go
    payment/
      payment_integration_test.go
  test/
    integration/
      testkit/
        suite_env.go
        fixtures.go
        kafka.go
      scenarios/
        checkout_flow_test.go
        refund_flow_test.go
      seeds/
        sql/
          base_schema.sql
          seed_users.sql
      helpers/
        assert.go
        http.go

Recommended split:

  • Domain-local integration tests:
    • domain/*/*_integration_test.go
  • Shared integration bootstrapping/helpers:
    • test/integration/testkit/*
  • Cross-domain end-to-end scenarios:
    • test/integration/scenarios/*
  • Seed data and helper assertions:
    • test/integration/seeds/*, test/integration/helpers/*

Run

  • Unit tests only:
    • go test ./testkit
  • Integration tests (requires Docker):
    • RUN_INTEGRATION_TESTS=1 go test -tags integration ./testkit -v
  • Consumer repo integration tests:
    • RUN_INTEGRATION_TESTS=1 go test -tags integration ./... -v

Documentation

Overview

Package testkit provides helpers for integration tests powered by Testcontainers.

Index

Constants

View Source
const IntegrationEnv = "RUN_INTEGRATION_TESTS"

IntegrationEnv is an optional environment variable gate for integration tests.

Variables

This section is empty.

Functions

func RequireDocker

func RequireDocker(t testing.TB)

RequireDocker skips the test when Docker is unavailable.

func RequireIntegration

func RequireIntegration(t testing.TB)

RequireIntegration skips the test unless RUN_INTEGRATION_TESTS is set to a truthy value.

Types

type EMQXContainer

type EMQXContainer struct {
	Container         testcontainers.Container
	Host              string
	DashboardPort     string
	MQTTPort          string
	DashboardEndpoint string
	Username          string
	Password          string
}

EMQXContainer contains connection details for a started EMQX container.

type EMQXOptions

type EMQXOptions struct {
	Image          string
	Username       string
	Password       string
	StartupTimeout time.Duration
}

EMQXOptions controls how the EMQX container is started.

type MinIOContainer

type MinIOContainer struct {
	Container       testcontainers.Container
	Host            string
	APIPort         string
	ConsolePort     string
	Endpoint        string
	ConsoleEndpoint string
	AccessKey       string
	SecretKey       string
}

MinIOContainer contains connection details for a started MinIO container.

func (*MinIOContainer) EndpointURL

func (c *MinIOContainer) EndpointURL() *url.URL

EndpointURL returns the endpoint URL as a parsed value.

func (*MinIOContainer) HostPort

func (c *MinIOContainer) HostPort() string

HostPort returns "host:port" without scheme for S3 clients expecting a custom endpoint resolver.

func (*MinIOContainer) Region

func (c *MinIOContainer) Region() string

Region returns the default MinIO region value when none is explicitly configured.

func (*MinIOContainer) Scheme

func (c *MinIOContainer) Scheme() string

Scheme returns endpoint scheme, defaulting to "http".

type MinIOOptions

type MinIOOptions struct {
	Image          string
	AccessKey      string
	SecretKey      string
	StartupTimeout time.Duration
}

MinIOOptions controls how the MinIO container is started.

type PostgresContainer

type PostgresContainer struct {
	Container testcontainers.Container
	Host      string
	Port      string
	Username  string
	Password  string
	Database  string
}

PostgresContainer contains connection details for a started PostgreSQL container.

func (*PostgresContainer) URL

func (c *PostgresContainer) URL() string

URL returns a postgres:// URL with sslmode disabled, suitable for local integration tests.

type PostgresOptions

type PostgresOptions struct {
	Image          string
	Username       string
	Password       string
	Database       string
	StartupTimeout time.Duration
}

PostgresOptions controls how the PostgreSQL container is started.

type RedisContainer

type RedisContainer struct {
	Container testcontainers.Container
	Host      string
	Port      string
	Password  string
}

RedisContainer contains connection details for a started Redis container.

func (*RedisContainer) Addr

func (c *RedisContainer) Addr() string

Addr returns host:port for Redis clients.

type RedisOptions

type RedisOptions struct {
	Image          string
	Password       string
	StartupTimeout time.Duration
}

RedisOptions controls how the Redis container is started.

type Suite

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

Suite is a test helper that owns the context and lifecycle of containers started during a test.

func NewSuite

func NewSuite(t testing.TB) *Suite

NewSuite creates an integration test suite and verifies Docker availability.

func (*Suite) Context

func (s *Suite) Context() context.Context

Context returns the suite context used to start and inspect containers.

func (*Suite) Endpoint

func (s *Suite) Endpoint(c testcontainers.Container, scheme string, port string) string

Endpoint builds a "<scheme>://<host>:<mapped-port>" endpoint for a container port.

func (*Suite) Host

func (s *Suite) Host(c testcontainers.Container) string

Host returns the host where container ports are exposed.

func (*Suite) MappedPort

func (s *Suite) MappedPort(c testcontainers.Container, port string) string

MappedPort returns the mapped host port for a container port.

func (*Suite) StartContainer

StartContainer starts a test container and registers automatic cleanup.

func (*Suite) StartEMQX

func (s *Suite) StartEMQX(opts EMQXOptions) *EMQXContainer

StartEMQX starts an EMQX container configured for dashboard API authentication.

func (*Suite) StartMinIO

func (s *Suite) StartMinIO(opts MinIOOptions) *MinIOContainer

StartMinIO starts a MinIO container and returns API and console connection details.

func (*Suite) StartPostgres

func (s *Suite) StartPostgres(opts PostgresOptions) *PostgresContainer

StartPostgres starts a PostgreSQL container and returns its connection details.

func (*Suite) StartRedis

func (s *Suite) StartRedis(opts RedisOptions) *RedisContainer

StartRedis starts a Redis container and returns its connection details.

Jump to

Keyboard shortcuts

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