
fix
Composable, lazily-evaluated test fixtures for Go — inspired by pytest fixtures.
go get codeberg.org/datek/fix
Features
- Evaluated once per test — a fixture called multiple times within the same
*testing.T returns the cached value.
- Composable — fixtures can depend on other fixtures; dependencies are resolved automatically.
- Zero dependencies — only the Go standard library.
Usage
Define a fixture with fix.New, passing a function that receives *testing.T and returns a value. Call the fixture inside a test to get the value.
var fixtureDB = fix.New(func(t *testing.T) DB {
t.Helper()
return NewTestDB(t)
})
var fixtureUserStore = fix.New(func(t *testing.T) UserStore {
t.Helper()
db := fixtureDB(t) // resolved once, cached for this test
return NewUserStore(db)
})
func TestCreateUser(t *testing.T) {
store := fixtureUserStore(t)
err := store.CreateUser("Alice")
// ...
}
Because each fixture is only evaluated once per *testing.T, calling fixtureDB(t) from multiple fixtures within the same test always returns the same instance — no accidental duplication of shared resources.
Dependency resolution
Fixtures can freely call other fixtures. The dependency graph is resolved lazily at test time:
var fixtureStatements = fix.New(func(t *testing.T) *[]string {
return &[]string{}
})
var fixtureMockDB = fix.New(func(t *testing.T) DB {
statements := fixtureStatements(t)
return NewMockDB(func(stmt string) error {
*statements = append(*statements, stmt)
return nil
})
})
var fixtureUserStore = fix.New(func(t *testing.T) UserStore {
return NewUserStore(fixtureMockDB(t))
})
All three fixtures share the same statements slice within a single test, making assertions straightforward.
Examples
A full working example (mock DB + user store) lives in the examples/ directory. Core behaviour is covered in fixture_test.go.
Requirements
Go 1.24 or later.