fabricate

package module
v0.0.0-...-0fd621c Latest Latest
Warning

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

Go to latest
Published: May 24, 2021 License: MIT Imports: 5 Imported by: 0

README

go-fabricate

Go Report Card PkgGoDev

Expressive struct generation for your Go tests.

Install

go get github.com/ckbaum/go-fabricate

Usage and Examples

Let's define a User struct:

type User struct {
  FirstName        string
  LastName         string
  Email            *string
  Verified         bool
  Friends          []*User
}

After defining a Fabricator, fake Users can easily be generated with random, non-zero data.

userFabricator := fabricate.Define(User{})

user := userFabricator.Fabricate().(*User)
// User{
//   FirstName: "Leqrewn",
//   LastName: "Rnzenfjq",
//   Email: &"Hnaeqwk",
//   Verified: true,
//   Friends: {&User{...}, &User{...},},
// }

With a barebones Fabricator like this one:

  • All fields with composite data types will be set to a random value
  • Pointers, slices and maps with composite element types will be populated
  • Fields of the same type as the fabricated struct will also be generated using the fabricator (to a configurable depth.)
Custom Field behavior

After adding some custom field definitions, our userFabricator can generate more realistic users:

userFabricator := fabricate.Define(
  User{},
).Field("Verified", func(_ fabricate.Session) interface{} {
  // have all our test users be verified
  return true
}).Field("FirstName", func(_ fabricate.Session) interface{} {
  // realistic names with help from a fake data package
  return gofakeit.FirstName()
})

user := userFabricator.Fabricate().(*User)
// User{
//   FirstName: "James",
//   LastName: "Poadarp",
//   Email: &"Ejasdklf",
//   Verified: true,
//   Friends: {&User{...}, &User{...},},
// }

Field definitions can reference the in-progress struct's other fields through the Session parameter. Here, Email references the in-progress struct (through Session) to incorporate the user's name.

userFabricator := fabricate.Define(
  User{},
).Fields([]string{"FirstName", "LastName"}, func(_ fabricate.Session) interface{} {
  // realistic names with help from a fake data package
  // this time, FirstName and LastName share a generator
  return gofakeit.FirstName()
}).Field("Email", func(session fabricate.Session) interface{} {
  // an email based on this user's name
  email := fmt.Sprintf(
    "%s.%s@gmail.com",
    session.Struct.(*User).FirstName,
    session.Struct.(*User).LastName,
  )
  return &email
})
  
user := userFabricator.Fabricate().(*User)
// User{
//   FirstName: "James",
//   LastName: "Joyce",
//   Email: &"james.joyce@gmail.com",
//   Verified: true,
//   Friends: {&User{...}, &User{...},},
// }
Use Traits to describe groups of properties

Variations of Fabricators can be defined with Traits, and applied at fabrication time.

userFabricator.Trait(
  "no email",
).Field("Email", func(_ fabricate.Session) interface{} {
  return nil
}).Field("Verified", func(_ fabricate.Session) interface{} {
  return false
})
  
user := userFabricator.Fabricate("no email").(*User)
// User{
//   FirstName: "James",
//   LastName: "Joyce",
//   Email: nil,
//   Verified: false,
//   Friends: {&User{...}, &User{...},},
// }

If you've defined multiple traits, they can be combined during fabrication:

userFabricator.Trait(
  "short name",
).Field("FirstName", func(_ fabricate.Session) interface{} {
  return "Em"
})
  
user := userFabricator.Fabricate("no email", "short name").(*User)
// User{
//   FirstName: "Em",
//   LastName: "Joyce",
//   Email: nil,
//   Verified: false,
//   Friends: {&User{...}, &User{...},},
// }

By defining a Registry that tracks multiple fabricators, nested Structs will automatically be generated using the appropriate Fabricator.

Consider two Structs , Author and Book

type Author struct {
  Name string
  Country string
  Books []*Book
}

type Book struct {
  Title string
  Language string
}

This time, we'll define a Registry and define our two fabricators into the Registry.

fabricatorRegistry := fabricate.NewRegistry()

authorFabricator := fabricatorRegistry.Define(
  Author{},
).Register(fabricatorRegistry)

bookFabricator := fabricatorRegistry.Define(
  Book{},
).Field("Language", func(_ fabricate.Session) interface{} {
  return []string{"English", "French"}[rand.Intn(2)]
}).Register(fabricatorRegistry)

Because these Fabricators are defined within the same Registry, the Books field in Author will be generated using bookFabricator by default.

authorFabricator.Fabricate().(Author)
// Author{
//   Name: "Ejasdlf",
//   Country: "Pasfaf",
//   Books: []*Book{
//     &Book{
//       Title: "Peafee",
//       Language: "English"
//     },
//     &Book{
//       Title: "Peafee",
//       Language: "French"
//     },
//     &Book{
//       Title: "Peafee",
//       Language: "French"
//     },
// }

License

The MIT License (MIT) - see LICENSE.md for more details

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Config

type Config struct {
	DefaultGeneratorsEnabled bool
	DefaultSliceLength       int
	DefaultMapSize           int
	DefaultStringLength      int
	GeneratePtrs             bool
	MaxRecursiveStructDepth  int
}

A Config defines certain behaviors of a Fabricator

DefaultGeneratorsEnabled: Whether default generators will be used for literals DefaultSliceLength: Default length of generated slices DefaultMapSize: Default size of generated maps DefaultStringLength: Default length of generated strings GeneratePtrs: Whether ptrs should be generated by default MaxRecursiveStructDepth: When a struct has a field that's a ptr to its own type, how deep it should generate

type Fabricator

type Fabricator struct {
	Template        interface{}
	FieldGenerators map[string]FieldGenerator
	Traits          map[string]Trait
	Registry        *Registry
	Config          *Config
	// contains filtered or unexported fields
}

A Fabricator generates structs matching the type of its Template

func Define

func Define(template interface{}) Fabricator

Define returns a new Fabricator for generating structs of the same type as the provided struct

func (Fabricator) Fabricate

func (f Fabricator) Fabricate(traitNames ...string) interface{}

Fabricate generates a new struct as configured by this Fabricator. Its return value should be type asserted to a pointer.

func (Fabricator) Field

func (f Fabricator) Field(fieldName string, generator FieldGenerator) Fabricator

Field returns a Fabricator that includes a function for generating a particular field.

func (Fabricator) Fields

func (f Fabricator) Fields(fieldNames []string, generator FieldGenerator) Fabricator

Fields returns a Fabricator that includes a function for generating multiple fields

func (Fabricator) Register

func (f Fabricator) Register(registry *Registry) Fabricator

Register returns a Fabricator that is associated with the provided Registry.

func (Fabricator) Trait

func (f Fabricator) Trait(traitName string) Trait

Trait defines a new Trait associated with this Fabricator

func (Fabricator) With

func (f Fabricator) With(newTemplate interface{}) Fabricator

With returns a Fabricator that will use any non-zero values of the provided struct in the structs that it generates.

func (Fabricator) WithConfig

func (f Fabricator) WithConfig(config *Config) Fabricator

WithConfig returns a Fabricator with the provided Config

func (Fabricator) ZeroFields

func (f Fabricator) ZeroFields(omitFieldNames ...string) Fabricator

ZeroFields returns a Fabricator that is set to generate a set of fields to their respective zero values

type FieldGenerator

type FieldGenerator func(Session) interface{}

A FieldGenerator is a function that generates a particular field using the Session

type Registry

type Registry struct {
	Fabricators map[string]*Fabricator
	Config      *Config
}

A Registry tracks multiple Fabricators, so that one Fabricator can use a different Fabricator to generate a struct type field.

func NewRegistry

func NewRegistry() *Registry

NewRegistry returns a new Registry

func (*Registry) Add

func (r *Registry) Add(fabricator *Fabricator)

Add registers an existing Fabricator to this Registry

func (*Registry) Define

func (r *Registry) Define(template interface{}) Fabricator

Define returns a new Fabricator registered to this Registry

func (*Registry) SetConfig

func (r *Registry) SetConfig(config *Config) *Registry

SetConfig sets this Registry's Config

type Session

type Session struct {
	Struct interface{}
	Config *Config
	Rand   *rand.Rand
	// contains filtered or unexported fields
}

A Session stores data pertaining to a particular call to Fabricate, including the in-progress struct

type Trait

type Trait struct {
	FieldGenerators map[string]FieldGenerator
}

A Trait contains an alternate set of FieldGenerators that can be used for a particular Fabrication, in place of the Fabricator's FieldGenerators.

func (Trait) Field

func (t Trait) Field(fieldName string, generator FieldGenerator) Trait

Field returns a Trait that includes a function for generating a particular field.

Jump to

Keyboard shortcuts

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