Lifecycle hooks
Hooks receive a pointer to the instance and the build’s context, and return an error. There are three, one per lifecycle point:
type Hook[T any] func(*T, BuildContext) errorAfterFaker
Section titled “AfterFaker”Runs immediately after generation, before any field configuration. It sees a fully generated value with none of your configuration applied yet.
fabricator.AfterFaker(func(person *Person, _ fabricator.BuildContext) error { person.Email = strings.ToLower(person.Email) return nil})On a factory using WithoutFaker, it still runs in the same position — against
the zero value.
AfterBuild
Section titled “AfterBuild”Runs after defaults and overrides, so it sees the finished value. This is where derived fields belong, because it can read whatever the test configured:
fabricator.AfterBuild(func(person *Person, _ fabricator.BuildContext) error { person.Email = strings.ToLower(person.FirstName) + "@example.com" return nil})It is also the way to reach a nested field, since dotted field paths are not supported:
fabricator.AfterBuild(func(order *Order, _ fabricator.BuildContext) error { order.Address.City = "Tel Aviv" return nil})AfterCreate
Section titled “AfterCreate”Runs after persistence, against the value the handler returned — so it observes whatever the store filled in, such as a generated ID.
fabricator.AfterCreate(func(person *Person, ctx fabricator.BuildContext) error { return index.Add(ctx.Iteration, person.ID)})For CreateBatch, it runs per instance, and each receives the iteration of the
build it came from.
Ordering and errors
Section titled “Ordering and errors”Hooks of the same kind run in the order they were registered. When a factory is extended, the base’s hooks run before the derived factory’s.
A hook returning an error aborts the build. The error is wrapped so the failing stage is named:
after build hook failed: email must not be emptyWith Build, that becomes a panic; with BuildE, it is returned.