When to use integration tests
Use integration tests to cover behavior across service boundaries, real databases, caches, and storage. These tests catch failures that mocks miss. Integration tests must document the cross-boundary guarantee they protect. Make it clear which production contract would be broken if the test failed, such as transaction rollback, idempotency, permission propagation, cache invalidation, or persistence after restart.Container patterns
Usepkg/testutil/containers for shared test containers. Helpers lazily start
containers through pkg/testutil/docker-compose.test.yaml and reuse one Docker
Compose project per worktree, so tests only start the services they need.
mise run test removes the shared containers for that worktree after the test
suite exits.
Restate is the exception to container reuse. containers.Restate starts a
server per test and removes it afterwards, because Restate identifies services
by name and those names come from protobuf packages. Two tests registering
their own workers on one server would overwrite each other’s routing and share
virtual object state.
A private Restate costs about a second to start. Since Go runs a package’s
tests sequentially, at most one such container per test binary is alive at a
time.
To inspect a failure, set UNKEY_TEST_KEEP_RESTATE=1. The container of a
failed test is left running and its admin URL is logged, so the invocation
journal and state stay queryable:
mise run test. Direct rask does not run the
cleanup trap from the mise task.
Test harness
Usepkg/testutil when you need a full service graph and seeded data: