Pytest-inspired fixture caching for Go tests with precise scope control, automatic cleanup, and no external dependencies.
Fixenv helps Go developers describe repeatable test environments once and reuse them safely across an entire test suite. Fixtures are cached per configurable scope, dependencies between fixtures are tracked automatically, and cleanup hooks make resource lifecycle management explicit.
- Features
- Installation
- Quick start
- Fixture scopes & caching
- Cleanup & ordering guarantees
- Documentation
- Example gallery
- Comparison with alternatives
- Project roadmap
- Contributing
- License
- Scope-aware caching – Run expensive setup only once per test, test tree, or package using
CacheOptions.Scope. - Deterministic cleanup – Register cleanup callbacks via
NewGenericResultWithCleanup; Fixenv invokes them when the owning scope ends. - Fixture dependency tree – Compose fixtures freely; cached results are shared across the dependency tree to avoid duplicated work.
- Skip-aware execution – Return
ErrSkipTestfrom fixtures to short-circuit tests without leaking resources. - Extensible environment – Embed
EnvTor implement theEnvinterface to customise behaviour for your project. - Parallel-ready fixtures – Caching and cleanup stay correct when tests call
t.Parallel; each parallel test receives its own safe environment. - Zero dependencies – Fixenv is a lightweight helper that plays well with the standard
testingpackage. - Starter fixtures included – Import
github.com/rekby/fixenv/sffor ready-to-use helpers like cancellable contexts, temporary directories, and TCP listeners.
go get github.com/rekby/fixenvFixenv follows Go modules semantic import versioning. Re-run the command above to upgrade to the latest tag.
package context_test
import (
"testing"
"github.com/rekby/fixenv"
"github.com/rekby/fixenv/sf"
)
func TestAutoCanceledContext(t *testing.T) {
e := fixenv.New(t)
ctx := sf.Context(e)
// Exercise your code under test with ctx.
// When the test finishes, the context is cancelled automatically.
}sf.Context ships with Fixenv and returns a cancellable context bound to the current test scope. When the test ends, the fixture automatically cancels the context through its registered cleanup—no manual teardown required.
package counter_test
import (
"testing"
"github.com/rekby/fixenv"
)
var globalCounter int
func counter(e fixenv.Env) int {
return fixenv.CacheResult(e, func() (*fixenv.GenericResult[int], error) {
globalCounter++
return fixenv.NewGenericResult(globalCounter), nil
})
}
func TestCounter(t *testing.T) {
e := fixenv.New(t)
if counter(e) != counter(e) {
t.Fatal("value must be cached within a single test scope")
}
}This pattern—create an environment with fixenv.New(t), wrap setup logic in fixenv.CacheResult, and optionally attach a cleanup—is the basis for all fixtures. Continue with the Getting started guide for step-by-step instructions and cleanup examples.
Fixtures default to ScopeTest: cached per testing.T. Subtests get their own scope unless you opt in to sharing. Override the scope through CacheOptions to share expensive setup more broadly:
| Scope | Lifetime | Typical use cases |
|---|---|---|
ScopeTest |
Current testing.T only |
Pure unit tests, short-lived resources |
ScopeTestAndSubtests |
Top-level test and all nested subtests | Reusing setup across a parent test and its subtests |
ScopePackage |
Entire package (requires TestMain) |
Shared databases, external services, heavy caches |
Scope names combine automatically with the fixture's call site to produce a stable cache key. You can extend the cache key with serialisable parameters via CacheOptions.CacheKey when a fixture accepts arguments.
Learn more in Scopes and lifetimes.
Each fixture may optionally return a cleanup callback. Fixenv registers the callback on the owning testing.T and guarantees LIFO execution when the scope ends. Cleanups run even if a test fails or is skipped via ErrSkipTest, making it safe to provision external services, create temporary files, or adjust global state.
See Cleanup and ordering for practical recipes.
The docs/ directory provides extended guides:
- Getting started – installation, basic fixtures, and first tests.
- Scopes and lifetimes – choosing the right cache level and structuring packages.
- Advanced fixtures – parameterised fixtures, dependency injection patterns, and custom environments.
- Cleanup and ordering – teardown techniques, deterministic ordering, and debugging tips.
- Example walkthroughs – narrative tours of the sample projects.
Explore runnable examples under examples/:
| Example | Highlights |
|---|---|
simple |
Minimal fixtures, scope defaults, and cleanup basics. |
custom_env |
Embedding EnvT in a domain-specific helper to expose project-specific fixtures. |
sf_helpers |
Using bundled fixtures from sf for contexts, temp directories, and local TCP listeners. |
simple_main_test |
Using TestMain to install package-level fixtures with cleanup control. |
Run the full set with:
go test ./examples/...Detailed walkthroughs and expected outputs live in docs/examples/README.md.
| Feature / Project | Fixenv | testify/suite |
dockertest |
gotest.tools/fs |
|---|---|---|---|---|
| Scoped caching (test / package) | ✅ | ⚪️ manual | ⚪️ manual | ⚪️ manual |
| Declarative fixture tree | ✅ | ⚪️ | ⚪️ | ⚪️ |
| Cleanup integration | ✅ (LIFO with fixtures) | ⚪️ (testing.T.Cleanup) |
✅ (containers) | ⚪️ (testing.T.Cleanup) |
| Skip-aware setup | ✅ (ErrSkipTest) |
⚪️ | ⚪️ | ⚪️ |
Works with plain testing.T |
✅ | ✅ | ✅ | ✅ |
| Extra runtime requirements | Go stdlib only | Go stdlib only | Docker daemon | Go stdlib only |
Fixenv focuses on composing reusable fixtures with deterministic lifecycle control. It complements assertion libraries and environment provisioning tools—you can mix Fixenv with them instead of choosing only one approach.
- Additional built-in helpers for temporary directories and HTTP servers.
- Optional tracing hooks for fixture execution and cache hits.
- Community-contributed examples covering databases, message queues, and cloud resources.
- Automatic detection of scope-mixing mistakes (e.g. invoking a test-scoped fixture from a package-scoped one) to surface lifecycle issues early.
Interested in a feature? Open an issue or start a discussion.
Contributions are welcome! Please:
- Fork the repository and create a feature branch.
- Include thorough automated tests with each code change.
- Run
go test ./...before opening a pull request. - Describe your changes in detail and link to related issues.
Bug reports and documentation improvements are also appreciated.
Licensed under the MIT License.