Fields and values
Field references
Section titled “Field references”Every way of configuring a field starts with a typed reference:
firstName := fabricator.FieldOf[Person, string]("FirstName")FieldOf[T, V] panics at the moment you call it if T has no field of that
name, if the field is unexported, or if it does not accept a V. That is
deliberate: the failure belongs at the reference, where the mistake is, rather
than at a build somewhere else.
Declaring references once as package-level variables keeps call sites short and moves the check to package initialisation:
var ( firstName = fabricator.FieldOf[Person, string]("FirstName") lastName = fabricator.FieldOf[Person, string]("LastName"))The escape hatch
Section titled “The escape hatch”UnsafeFieldOf[T, V] builds a reference without checking anything. The same
checks then run during Build, and surface as a build error rather than a
panic at construction:
ref := fabricator.UnsafeFieldOf[Person, string]("Nickname")_, err := factory.BuildE() // unknown field "Nickname"Reach for it only when the field name genuinely is not known statically.
Embedded and nested fields
Section titled “Embedded and nested fields”Fields promoted from an embedded struct work by their promoted name:
type Timestamps struct{ CreatedAt time.Time }type Post struct { Timestamps Title string}
createdAt := fabricator.FieldOf[Post, time.Time]("CreatedAt")If two embedded structs promote the same name, the reference is ambiguous and
FieldOf reports it as an unknown field, matching Go’s own resolution rules.
Dotted paths such as "Author.Name" are not supported and fail with an
explicit error. Use an AfterBuild hook to reach
into a nested value.
Static values
Section titled “Static values”fabricator.Value(firstName, "Moishe")Applies to every build from the factory.
Value is also the cheaper form. Because the value is fixed when you configure
it, Fabricator prepares it once at that point; a Field provider has to be
called and its result boxed on every build, which costs an allocation per field
per build. Prefer Value whenever the value does not depend on the build.
Providers
Section titled “Providers”Field takes a function called once per build, which receives the build’s
context:
fabricator.Field(email, func(ctx fabricator.BuildContext) string { return fmt.Sprintf("user%d@example.com", ctx.Iteration)})BuildContext carries Iteration, the zero-based count of builds from this
factory, and FieldName, the field being set. Iteration is consumed after
faker succeeds and before defaults, overrides, and hooks run.
Sequences
Section titled “Sequences”Sequence cycles values by iteration, which is the common case of a provider:
fabricator.Field(role, fabricator.Sequence("admin", "editor", "viewer"))
factory.Batch(5) // admin, editor, viewer, admin, editorBecause the value is chosen by the factory’s iteration rather than by a counter
of its own, every Sequence on a factory advances in lockstep, and
ResetCounter restarts them all together.
Sequence copies the values you give it, so mutating the slice you passed does
not change what the factory generates. It panics if given no values.
Per-build overrides
Section titled “Per-build overrides”Override and OverrideField are the build-scoped forms of Value and
Field:
factory.Build(fabricator.Override(lastName, "Zuchmir"))factory.Batch(3, fabricator.OverrideField(role, fabricator.Sequence("a", "b")))Overrides are applied after the factory’s own configuration, so they win.
Order of precedence
Section titled “Order of precedence”For a single build, in order:
- faker fills every field, unless the factory uses
WithoutFaker AfterFakerhooks run- the factory’s
ValueandFieldconfiguration is applied - the call’s
OverrideandOverrideFieldoptions are applied AfterBuildhooks run
Configuring the same field twice keeps only the last configuration, and the
superseded one does not run at all. That matters when the superseded provider
has side effects — a superseded Subfactory does not build a child and throw
it away.
Nil values
Section titled “Nil values”A provider may return nil for a field that can hold it — a pointer, slice,
map, channel, function, or interface. The field is set to its zero value.
Returning nil for any other field is an error:
field "ID" expects int, got nilSkipping generation
Section titled “Skipping generation”WithoutFaker turns generation off, so builds start from T’s zero value and
only configured fields are populated:
factory := fabricator.New(Person{}, fabricator.WithoutFaker[Person](), fabricator.Value(firstName, "Moishe"),)
factory.Build() // Person{FirstName: "Moishe"}, everything else zeroUse it when random values in unconfigured fields are noise rather than
coverage. It is also dramatically cheaper, because faker’s reflective walk over
T is what a build actually costs.
AfterFaker hooks still run in the same position, against the zero value.
WithFakerOptions has no effect on a factory that skips generation, and
WithFaker turns generation back on — useful when
extending a base that skips it.
Tuning generation
Section titled “Tuning generation”Faker options pass straight through:
import "github.com/go-faker/faker/v4/pkg/options"
fabricator.WithFakerOptions[Person]( options.WithIgnoreInterface(true), options.WithRandomMapAndSliceMinSize(1),)WithIgnoreInterface(true) is worth knowing about: faker cannot generate a
value for a bare any field and fails the build without it.
Faker also honours faker:"..." struct tags on the model itself:
type Person struct { Email string `faker:"email"` ID string `faker:"uuid_hyphenated"`}