SkillAgentSearch skills...

Gofacto

A strongly-typed and user-friendly factory library for Go

Install / Use

/learn @eyo-chen/Gofacto
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

Go Reference Go Report Card Coverage Status

gofacto

gofacto is a strongly-typed and user-friendly factory library for Go, designed to simplify the creation of mock data. It offers:

  • Intuitive and straightforward usage
  • Strong typing and type safety
  • Flexible data customization
  • Support for various databases and ORMs
  • Basic and multi-level association relationship support

 

Installation

go get github.com/eyo-chen/gofacto

 

Quick Start

You can find more examples in the examples folder.

Let's consider two structs: Customer and Order. Order struct has a foreign key CustomerID because a customer can have many orders.

type Customer struct {
    ID      int
    Gender  Gender     // custom defined type
    Name    string
    Email   *string
    Phone   string
}

type Order struct {
    ID          int
    CustomerID  int       `gofacto:"foreignKey,struct:Customer"`
    OrderDate   time.Time
    Amount      float64
}

// Init a Customer factory
customerFactory := gofacto.New(Customer{}).
                           WithDB(mysqlf.NewConfig(db)) // Assuming db is a database connection

// Create and insert a customer
customer, err := customerFactory.Build(ctx).Insert()

// Create and insert two female customers
customers, err := customerFactory.BuildList(ctx, 2).
                                  Overwrite(Customer{Gender: Female}).
                                  Insert()
// customers[0].Gender == Female
// customers[1].Gender == Female

// Init a Order factory
orderFactory := gofacto.New(Order{}).
                        WithDB(mysqlf.NewConfig(db))

// Create and insert an order with one customer
c := Customer{}
order, err := orderFactory.Build(ctx).
                           WithOne(&c).
                           Insert()
// order.CustomerID == c.ID

// Create and insert two orders with two customers
c1, c2 := Customer{}, Customer{}
orders, err := orderFactory.BuildList(ctx, 2).
                            WithMany([]interface{}{&c1, &c2}).
                            Insert()
// orders[0].CustomerID == c1.ID
// orders[1].CustomerID == c2.ID

Note: All fields in the returned struct are populated with non-zero values, and ID field is auto-incremented by the database.

 

Usage

Initialize

Use New to initialize the factory by passing the struct you want to create mock data for.

factory := gofacto.New(Order{})

Build & BuildList

Use Build to create a single value, and BuildList to create a list of values.

order, err := factory.Build(ctx).Get()
orders, err := factory.BuildList(ctx, 2).Get()

Get method returns the struct(s) without inserting them into the database. All fields are populated with non-zero values.

Insert

Use Insert to insert values into the database.<br> Insert method inserts the struct into the database and returns the struct with ID field populated with the auto-incremented value.<br>

order, err := factory.Build(ctx).Insert()
orders, err := factory.BuildList(ctx, 2).Insert()

Find out more examples.

Overwrite

Use Overwrite to set specific fields.<br> The fields in the struct will be used to overwrite the fields in the generated struct.

order, err := factory.Build(ctx).Overwrite(Order{Amount: 100}).Insert()
// order.Amount == 100

When building a list of values, Overwrite is used to overwrite all the list of values, and Overwrites is used to overwrite each value in the list of values.

orders, err := factory.BuildList(ctx, 2).Overwrite(Order{Amount: 100}).Insert()
// orders[0].Amount == 100
// orders[1].Amount == 100

orders, err := factory.BuildList(ctx, 2).Overwrites(Order{Amount: 100}, Order{Amount: 200}).Insert()
// orders[0].Amount == 100
// orders[1].Amount == 200

Note: Explicit zero values are not overwritten by default. Use SetZero or SetTrait for this purpose.

order, err := factory.Build(ctx).Overwrite(Order{Amount: 0}).Insert()
// order.Amount != 0

Find out more examples.

SetTrait

When initializing the factory, use WithTrait method to set the trait functions and the corresponding keys. Then use SetTrait method to apply the trait functions when building the struct.

func setFemale(c *Customer) {
  c.Gender = Female
}
factory := gofacto.New(Order{}).
                   WithTrait("female", setFemale)

customer, err := factory.Build(ctx).SetTrait("female").Insert()
// customer.Gender == Female

When building a list of values, SetTrait is used to apply one trait function to all the list of values, and SetTraits is used to apply multiple trait functions to the list of values.

func setFemale(c *Customer) {
  c.Gender = Female
}
func setMale(c *Customer) {
  c.Gender = Male
}
factory := gofacto.New(Order{}).
                   WithTrait("female", setFemale).
                   WithTrait("male", setMale)

customers, err := factory.BuildList(ctx, 2).SetTrait("female").Insert()
// customers[0].Gender == Female
// customers[1].Gender == Female

customers, err := factory.BuildList(ctx, 2).SetTraits("female", "male").Insert()
// customers[0].Gender == Female
// customers[1].Gender == Male

Find out more examples.

SetZero

Use SetZero to set specific fields to zero values.<br> SetZero method with Build accepts multiple string as the field names, and the fields will be set to zero values when building the struct.<br> SetZero method with BuildList accepts an index and multiple string as the field names, and the fields will be set to zero values when building the struct at the index.

customer, err := factory.Build(ctx).SetZero("Email", "Phone").Insert()
// customer.Email == nil
// customer.Phone == ""

customers, err := factory.BuildList(ctx, 2).SetZero(0, "Email", "Phone").Insert()
// customers[0].Email == nil
// customers[0].Phone == ""
// customers[1].Email != nil
// customers[1].Phone != ""

Find out more examples.

WithOne & WithMany

When there is the associations relationship between the structs, use WithOne and WithMany methods to build the associated structs.<br> Before using WithOne and WithMany methods, make sure setting the correct tag in the struct.

type Order struct {
  ID          int
  CustomerID  int       `gofacto:"foreignKey,struct:Customer"`
  OrderDate   time.Time
  Amount      float64
}

You can find more details about the tag format in foreignKey tag.

// build an order with one customer
c := Customer{}
order, err := factory.Build(ctx).WithOne(&c).Insert()
// order.CustomerID == c.ID

// build two orders with two customers
c1 := Customer{}
c2 := Customer{}
orders, err := factory.BuildList(ctx, 2).WithMany([]interface{}{&c1, &c2}).Insert()
// orders[0].CustomerID == c1.ID
// orders[1].CustomerID == c2.ID

// build an order with only one customer
c1 := Customer{}
orders, err := factory.BuildList(ctx, 2).WithOne(&c1).Insert()
// orders[0].CustomerID == c1.ID
// orders[1].CustomerID == c1.ID

If there are multiple level association relationships, both WithOne and WithMany methods can also come in handy.<br> Suppose we have a following schema:

type Expense struct {
	ID         int
	UserID     int `gofacto:"foreignKey,struct:User"`
	CategoryID int `gofacto:"foreignKey,struct:Category,table:categories"`
}

type Category struct {
	ID     int
	UserID int `gofacto:"foreignKey,struct:User"`
}

type User struct {
	ID int
}

We can build the Expense struct with the associated User and Category structs by using WithOne and WithMany methods.

// build one expense with one user and one category
user := User{}
category := Category{}
expense, err := factory.Build(ctx).WithOne(&user).WithOne(&category).Insert()
// expense.UserID == user.ID
// expense.CategoryID == category.ID
// category.UserID == user.ID

// build two expenses with two users and two categories
user1 := User{}
user2 := User{}
category1 := Category{}
category2 := Category{}
expenses, err := factory.BuildList(ctx, 2).WithMany([]interface{}{&user1, &user2}).WithMany([]interface{}{&category1, &category2}).Insert()
// expenses[0].UserID == user1.ID
// expenses[0].CategoryID == category1.ID
// expenses[1].UserID == user2.ID
// expenses[1].CategoryID == category2.ID
// category1.UserID == user1.ID
// category2.UserID == user2.ID

This is one of the most powerful features of gofacto, it helps us easily build the structs with the complex associations relationships as long as setting the correct tags in the struct.<br>

Find out more examples.

<details> <summary>Best Practice to use <code>WithOne</code> & <code>WithMany</code></summary> <ul> <li>Must pass the struct pointer to <code>WithOne</code> or <code>WithMany</code></li> <li>Must pass same type of struct pointer to <code>WithMany</code></li> <li>Do not pass struct with cyclic dependency</li> </ul>
// Do not do this:
type A struct {
    B_ID int `gofacto:"foreignKey,struct:B"`
}
type B struct {
    A_ID int `gofacto:"foreignKey,struct:A"`
}
</details>

Reset

Use Reset method to reset the factory.

factory.Reset()

Reset

View on GitHub
GitHub Stars30
CategoryDevelopment
Updated4mo ago
Forks0

Languages

Go

Security Score

92/100

Audited on Nov 11, 2025

No findings