Fabricator
May 28, 2026 ยท View on GitHub
Fabricator is a generics-first Go library for building typed test data factories.
Installation
go get github.com/Goldziher/fabricator/v2
Basic Usage
type Person struct {
ID int
FirstName string
LastName string
}
firstName := fabricator.FieldOf[Person, string]("FirstName")
lastName := fabricator.FieldOf[Person, string]("LastName")
id := fabricator.FieldOf[Person, int]("ID")
factory := fabricator.New(
Person{},
fabricator.Value(firstName, "Moishe"),
fabricator.Field(id, func(ctx fabricator.BuildContext) int {
return ctx.Iteration + 1
}),
)
person := factory.Build(fabricator.Override(lastName, "Zuchmir"))
See example_test.go for compile-tested examples.
Typed Fields
Factory customization uses typed field descriptors:
firstName := fabricator.FieldOf[Person, string]("FirstName")
factory := fabricator.New(
Person{},
fabricator.Value(firstName, "Moishe"),
)
FieldOf[T, V] validates that the field exists, is exported, and accepts V. UnsafeFieldOf is available as an explicit runtime-checked escape hatch.
The old map[string]any defaults and overrides API has been removed in v2.
Errors And Panics
Every build and persistence method has an error-returning form:
person, err := factory.BuildE()
people, err := factory.BatchE(5)
created, err := factory.CreateE(ctx)
createdBatch, err := factory.CreateBatchE(ctx, 5)
Build, Batch, Create, and CreateBatch remain panic-on-error convenience wrappers for terse tests.
Lifecycle Hooks
Hooks can inspect or mutate the generated object:
factory := fabricator.New(
Person{},
fabricator.AfterBuild(func(person *Person, ctx fabricator.BuildContext) error {
person.Email = strings.ToLower(person.FirstName) + "@example.com"
return nil
}),
)
AfterFaker runs after faker data generation, AfterBuild runs after defaults and overrides, and AfterCreate runs after persistence.
Subfactories
Nested factories are first-class providers:
petFactory := fabricator.New(Pet{}, fabricator.Value(fabricator.FieldOf[Pet, string]("Name"), "Flippy"))
personFactory := fabricator.New(
Person{},
fabricator.Field(fabricator.FieldOf[Person, *Pet]("FavoritePet"), fabricator.PtrSubfactory(petFactory)),
fabricator.Field(fabricator.FieldOf[Person, []Pet]("Pets"), fabricator.SliceSubfactory(petFactory, 2)),
)
Use Subfactory for value fields, PtrSubfactory for pointer fields, and SliceSubfactory for slices. The *With variants let child overrides and slice sizes depend on the parent BuildContext.
Faker Options
Fabricator delegates base data generation to github.com/go-faker/faker/v4:
factory := fabricator.New(
Person{},
fabricator.WithFakerOptions[Person](options.WithIgnoreInterface(true)),
)
Persistence
Persistence handlers are context-aware and return errors:
type PersistenceHandler[T any] interface {
Save(ctx context.Context, instance T) (T, error)
SaveMany(ctx context.Context, instances []T) ([]T, error)
}
Counter Semantics
The counter is race-safe. Iterations are consumed after faker succeeds and before defaults, overrides, and hooks run. SetCounter and ResetCounter are race-safe but are not coordination primitives for concurrent builds.
Limits
Factories are for non-pointer struct types. Nested dotted field paths are not supported; use hooks for nested mutation.
Development
This repository uses Go 1.26, golangci-lint v2, prek, gitfluff, and ai-rulez.
task setup
task test
task test:race
task check
task lint