facto

package module
v0.0.0-...-69b27b7 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 28, 2022 License: Apache-2.0 Imports: 13 Imported by: 0

README

Facto

Facto is a fixtures library with a definition syntax. It aims to allow Go developers to define fixtures in a declarative way and using the Go language as the means to describe the fixtures instead of TOML/JSON/YAML. It is inspired by factory_bot.

Your first Factory

Facto exposes a small API to register and use factories. The following example shows how to create a factory named User:

//  in factories/user.go
package factories

import (
    "github.com/wawandco/facto"
)

func UserFactory(h facto.Helper) facto.Product {
    user := User{
        Name: h.Faker.Name(),
    }

    return facto.Product(user)
}

One these are added we can use our factories in our tests, e.g:

//  in users/user_test.go
package user_test

import (
    "your/package/factories"
)

func TestUser(t *testing.T) {
    user := facto.Build(factories.UserFactory).(models.User)
    err := db.Create(&user)
    // use user for test purposes ...

    // Another alternative is to use create, which will attempt at creating the object directly in your database.
    // In which case the user variable has already been stored in the database.
    p, err := facto.Create("User")
    user := p.(models.User)
    // use user for test purposes ...

}
Building/Creating N

Sometimes you need to build more than one instance of an object, in that case. Facto provides 2 functions to do that: BuildN and CreateN.

//  in test/user_test.go
package user_test

func TestUser(t *testing.T) {
    users := facto.BuildN(factories.User, 10).([]models.User)
    // use users for test purposes ...

    // you can also create N Users
    p, err := facto.CreateN(factories.User, 10)
    // make sure you check the error
    users := p.([]models.User)
}

When running BuildN you can use the Index variable to get the index of the object being built. This is useful when you need to build a list of objects and you want to know which one is being built.

//  in factories/user.go
package factories

import (
    "fmt"
    "github.com/wawandco/facto"
)

func UserFactory(h facto.Helper) facto.Product {
    user := User{
        Name: fmt.Sprintf("User %d", f.Index),
    }

    return facto.Product(user)
}

When the factory gets called individually Index will be 0.

Dependent factories

Another case is when you need to build an object that depends on another object. You can use the factory.Helper parameter on your factory to get the object you need:

//  in factories/event.go
package factories
import (
    "github.com/wawandco/facto"
)

func UserFactory(h facto.Helper) facto.Product {
    user := User{
        Name: h.Faker.FirstName(),
    }

    return facto.Product(user)
}

func EventFactory(h facto.Helper) facto.Product {
    event := Event{
        // Here we pull the User from the helper given we know there is a
        // factory for it. can use it like this:
        User: facto.Build(UserFactory).(User),
        Type: "Something",
    }

    return facto.Product(event)
})

Another case here is when you need to reference the ID of the previous object. You can use the factory.Helper parameter on your factory to build a Named UUID:

//  in factories/user.go
package factories
import (
    "github.com/wawandco/facto"
)

func UserFactory(h facto.Helper) facto.Product {
    user := User{
        // owner_id will be assigned the generated UUID
        // and any
        ID: h.NamedUUID("owner_id"),
        Name: h.Faker.FirstName(),
    }

    return facto.Product(user)
}

//  in factories/event.go
package factories
import (
    "github.com/wawandco/facto"
)

func EventFactory(h facto.Helper) facto.Product {
    event := Event{
        // Here we pull the User from the helper given we know there is a
        // factory that adds it. If there was not one it would be generated new.
        UserID: h.NamedUUID("owner_id"),
        Type: "Something",
    }

    return facto.Product(event)
})
Faking data

Sometimes you need to generate data that is not real but at least looks similar to what the real data will be. To solve that need Facto provides the Fake method within the facto.Helper, it can be used within the factory it to generate fake data. for example:

//  in factories/event.go
package factories
import (
    "github.com/wawandco/facto"
)

func EventFactory(h facto.Helper) facto.Product {
    event := Event{
        Name: h.Faker.FirstName(),
        Type: "Sports",
        ContactEmail: h.Email(),
        Company: h.Faker.Company(),
        Address: h.Faker.Address(),
    }

    return facto.Product(event)
})

The fake data generation comes by courtesy of Gofakeit The full list of available fake data generators can be found in here.

One of

Another thing you could do with Facto is randomize the selection from a list of passed elements. For example:

//  in factories/event.go
package factories
import (
    "github.com/wawandco/facto"
)

func EventFactory(h facto.Helper) facto.Product {
    event := Event{
        Name: h.Faker.FirstName(),
        // You can pass here a list of elements to randomly select
        // from and the facto helper will pick one of these.
        Type: f.OneOf(TypeSports, TypeMusic, TypeConcert).(EventType),
        ...
    }

    return facto.Product(event)
})
The CLI

The Facto CLI is a simple command line tool that allows you to generate fixtures files. To install it you can use the following command:

go get github.com/wawandco/facto/cmd/facto@latest

The CLI allows you to generate factory files based on a given name. For example:

facto generate user
# Generates factories/user.go
Facto & Ox

Facto provides a plugin for the Ox CLI plugin system. To use it add the following to your cmd/ox/main.go file:

import (
    ...
    fox "github.com/wawandco/facto/ox" // Add the facto ox package, we called it fox here 😉
)

func main() {
    // You may have other plugins here.
    //...
    cli.Use(fox.Plugin{}) // Add the facto Ox plugin.

    err := cli.Run(context.Background(), os.Args)
	if err != nil {
		log.Fatal(err)
	}
}

Once added you can use facto with Ox within the generate command, you can see that invoking the generate command will list factory as a generator.

$ ox generate
[info] Using cmd/ox/main.go

Available Generators:

  Name          Plugin
  ----          ------
  ox            ox/generate-cli-main
  template      buffalo/generate-template
  model         buffalo/generate-model
  action        buffalo/generate-action
  resource      buffalo/generate-resource
  task          grift/generate-task
  migration     liquo/generate-migration
  factory       facto ## (this is the one you are looking for)

Which you can then use to generate your factories with:

$ ox generate factory [name]

Pending
  • Create API: Create a new object in the database.
  • Review terminology from Factory bot.
  • Explain the "Magic" constraints and some principles.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Generate

func Generate(root string, args []string) error

Generate a factory on a given root directory and a name for it (passed in args).

Types

type Factory

type Factory func(f Helper) Product

Factory represents the builder of a Product.

type Helper

type Helper struct {
	// Faker instance to provide the ability to create fake data to the helper.
	Faker *gofakeit.Faker

	// Index is useful when creating N elements, this
	// Allow to differentiate the elements by the index.
	Index int
}

Helper gets injected into the factory and provides convenience methods for the fixtures.

func NewHelper

func NewHelper() Helper

func (Helper) NamedUUID

func (h Helper) NamedUUID(name string) uuid.UUID

NamedUUID is a helper to create a UUID and keep it in the Facto context for later use this could come handy for database relations.

func (Helper) OneOf

func (h Helper) OneOf(values ...interface{}) interface{}

One of the passed elements, this method is useful when you have enum values and you want the falue of a field to be one of the possible values. e.g. ... u := User{ // here the value of the field is one of the passed elements.

	Status: OneOf(UserStatusActive, UserStatusInactive).(UserStatus)
}

type Product

type Product interface{}

Product is an abstract representation of a factory is able to built.

func Build

func Build(f Factory) Product

Build requests the factory identified by factoryName to build a product.

func BuildN

func BuildN(f Factory, n int) Product

BuildN requests the factory identified by factoryName to build n elements of a product.

Directories

Path Synopsis
cmd
package ox provides a plugin that can be used to generate factories with the OX cli generate command.
package ox provides a plugin that can be used to generate factories with the OX cli generate command.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL