go-migrationgo-migration
Factories
Documentation

Named States

Define factory variants using named states with f.State() and apply them with f.WithState().

Named States

Named states let you define variants of a factory that override specific fields. This is useful for creating different "types" of the same struct — like an admin user, an unverified account, or a published post.

Defining States

Use f.State() to register a named state on a factory. The state receives a factory.Faker and the base instance, and returns a modified instance with the desired overrides:

go
f.State(name string, fn func(faker factory.Faker, base T) T)
MethodPurpose
f.State(name, fn)Registers a named state modifier that receives the base instance
f.WithState(name)Returns a new factory that applies the named state on Make

Example: User States

factories/user_factory.go
package factories

import (
    "github.com/gopackx/go-migration/seeder/factory"
)

type User struct {
    ID       int
    Name     string
    Email    string
    Role     string
    Verified bool
}

var UserFactory = factory.NewFactory(func(f factory.Faker) User {
    return User{
        ID:       f.IntBetween(1, 10000),
        Name:     f.Name(),
        Email:    f.Email(),
        Role:     "user",
        Verified: true,
    }
})

func init() {
    // Admin state — overrides Role to "admin"
    UserFactory.State("admin", func(f factory.Faker, base User) User {
        base.Role = "admin"
        return base
    })

    // Unverified state — overrides Verified to false
    UserFactory.State("unverified", func(f factory.Faker, base User) User {
        base.Verified = false
        return base
    })
}

Applying States

Use WithState() to get a factory that produces instances using the named state's builder:

go
// Default user — Role: "user", Verified: true
user := UserFactory.Make()

// Admin user — Role: "admin", Verified: true
admin := UserFactory.WithState("admin").Make()

// Unverified user — Role: "user", Verified: false
unverified := UserFactory.WithState("unverified").Make()

WithState() returns a new factory instance, so the original factory remains unchanged.

Batch Generation with States

States work with MakeMany() for generating multiple instances:

go
// Generate 10 admin users
admins := UserFactory.WithState("admin").MakeMany(10)

// Generate 50 unverified users
unverifiedUsers := UserFactory.WithState("unverified").MakeMany(50)

Using States in Seeders

States are especially useful in seeders where you need different data profiles:

seeders/user_seeder.go
package seeders

import (
    "database/sql"
    "fmt"

    "myapp/factories"
)

type UserSeeder struct{}

func (s *UserSeeder) Run(db *sql.DB) error {
    // Seed 5 admin users
    admins := factories.UserFactory.WithState("admin").MakeMany(5)
    for _, u := range admins {
        _, err := db.Exec(
            "INSERT INTO users (name, email, role, verified) VALUES ($1, $2, $3, $4)",
            u.Name, u.Email, u.Role, u.Verified,
        )
        if err != nil {
            return fmt.Errorf("failed to seed admin: %w", err)
        }
    }

    // Seed 100 regular users
    users := factories.UserFactory.MakeMany(100)
    for _, u := range users {
        _, err := db.Exec(
            "INSERT INTO users (name, email, role, verified) VALUES ($1, $2, $3, $4)",
            u.Name, u.Email, u.Role, u.Verified,
        )
        if err != nil {
            return fmt.Errorf("failed to seed user: %w", err)
        }
    }

    return nil
}

States receive the base instance produced by the default definition, so you only need to override the fields you want to change.

What's Next?