Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/goldziher/fabricator
Golang factories for mock data generation
https://github.com/goldziher/fabricator
factories factory faker go golang mock
Last synced: 11 days ago
JSON representation
Golang factories for mock data generation
- Host: GitHub
- URL: https://github.com/goldziher/fabricator
- Owner: Goldziher
- License: mit
- Created: 2021-11-28T19:30:21.000Z (almost 3 years ago)
- Default Branch: main
- Last Pushed: 2024-05-07T22:53:54.000Z (6 months ago)
- Last Synced: 2024-10-10T23:26:21.503Z (28 days ago)
- Topics: factories, factory, faker, go, golang, mock
- Language: Go
- Homepage:
- Size: 53.7 KB
- Stars: 20
- Watchers: 4
- Forks: 2
- Open Issues: 3
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
- Security: SECURITY.md
Awesome Lists containing this project
README
# Fabricator
[![Go Report Card](https://goreportcard.com/badge/github.com/Goldziher/fabricator)](https://goreportcard.com/report/github.com/Goldziher/fabricator)
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=Goldziher_fabricator&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=Goldziher_fabricator)
[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=Goldziher_fabricator&metric=coverage)](https://sonarcloud.io/summary/new_code?id=Goldziher_fabricator)
[![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=Goldziher_fabricator&metric=sqale_rating)](https://sonarcloud.io/summary/new_code?id=Goldziher_fabricator)
[![Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=Goldziher_fabricator&metric=reliability_rating)](https://sonarcloud.io/summary/new_code?id=Goldziher_fabricator)
[![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=Goldziher_fabricator&metric=security_rating)](https://sonarcloud.io/summary/new_code?id=Goldziher_fabricator)Fabricator is a library for test data generation using structs and Go 1.18 generics. Its API is inspired by similar
libraries in other languages (e.g. [Pydantic-Factories](https://github.com/Goldziher/pydantic-factories)
, [Interface-Forge](https://github.com/Goldziher/interface-forge)), which was not possible in Go before the introduction
of generics.## Installation
```shell
go get -u github.com/Goldziher/fabricator
```## Example
```golang
package some_testimport (
"testing""github.com/Goldziher/fabricator"
"github.com/stretchr/testify/assert"
)type Pet struct {
Name string
Species string
}type Person struct {
Id int
FirstName string
LastName string
Pets []Pet
FavoritePet Pet
}var personFactory = fabricator.New[Person](Person{})
func TestSomething(t *testing.T) {
personInstance := personFactory.Build()
assert.IsType(t, Person{}, personInstance)
assert.NotZero(t, personInstance.Id)
assert.NotZero(t, personInstance.FirstName)
assert.NotZero(t, personInstance.LastName)
assert.NotZero(t, personInstance.Pets)
assert.NotZero(t, personInstance.FavoritePet)
}
```## Defining Factories
Defining a factory is very simple. Let's assume our app has a package called `types` where we define some struct, and
another package called `testhelpers` where we have some shared testing utilities.```golang
package typestype Pet struct {
Name string
Species string
}type Person struct {
Id int
FirstName string
LastName string
Pets []Pet
FavoritePet Pet
}
```Since factories can be reused, it's a good idea to define them in a specific package from which they can be imported. In
the case of our imaginary app, this will be the `testhelpers` package:```golang
package testhelpersimport (
"github.com/Goldziher/fabricator""github/someName/types"
)var PersonFactory = fabricator.New[types.Person](types.Person{})
```Note: If we have use for a Pet without its nesting inside Person, we might also want to define a PetFactory, but this is
not required in this example, since a slice of pets will be generated inside the `PersonFactory`.We could also pass an options object when defining the factory, setting the factory's `Defaults` and
a `PersistenceHandler` function.### Factory Defaults
```golang
package testhelpersimport (
"github.com/Goldziher/fabricator""github/someName/types"
)var PersonFactory = fabricator.New[types.Person](types.Person{}, fabricator.Options[types.Person]{
Defaults: map[string]any{
"FirstName": "Moishe",
"LastName": "Zuchmir",
"Pets": func(iteration int, fieldName string) interface{} {
pets := []types.Pet{}
if iteration%2 == 0 {
pets = append(pets, types.Pet{
"Flippy",
"Dolphin",
})
}
return pets
},
},
})
```As you can see above, the factory receives a `Defaults` object that maps struct field names, as map keys, to either
pre-specified values, or factory functions.While factory functions are more verbose, they are a powerful way to generate data and they can of course be shared
across different fields or even different factories.The signature for a factory function is `func(iteration int, fieldName string) interface{}`, with `iteration` being the
current value of the factory's internal counter, and `fieldName` being the name of the specific struct field for which a
value is being generated.### Persistence Handler
When defining a factory, you can pass a `PersistenceHandler`, that is, a struct conforming to
the `fabricator.PersistenceHandler` interface:```golang
package fabricatortype PersistenceHandler[T any] interface {
Save(instance T) T
SaveMany(instance []T) []T
}
```With a persistence handler defined for the factory, you can call the `.Create` and `.CreateBatch` methods which build
and then persist the data in one command. For example:```golang
package testhelpersimport (
"github.com/Goldziher/fabricator""github/someName/db"
"github/someName/types"
)type MyPersistenceHandler[T any] struct{}
func (handler MyPersistenceHandler[T]) Save(instance T) T {
db.Create(&instance)
return instance
}func (handler MyPersistenceHandler[T]) SaveMany(instances []T) []T {
db.Create(&instances)
return instances
}var PersonFactory = fabricator.New[types.Person](types.Person{}, fabricator.Options[types.Person]{
PersistenceHandler: MyPersistenceHandler[types.Person]{},
})
```## Factory Methods
Once a factory is defined it exposes the following methods:
### Build
`func (factory *Factory[T]) Build(overrides ...map[string]any) T`
Build creates a single instance of the factory's model:
```golang
package test_somethingimport (
"testing""github/someName/testhelpers"
)func TestSomething(t *testing.T) {
person := testhelpers.PersonFactory.Build()
// ...
}
```You can pass to build a mapping of override values, this works exactly like the factory defaults, for example:
```golang
package test_somethingimport (
"testing""github/someName/types"
"github/someName/testhelpers"
)func TestSomething(t *testing.T) {
person := testhelpers.PersonFactory.Build(map[string]any{
"FirstName": "Moishe",
"LastName": "Zuchmir",
"Pets": func(iteration int, fieldName string) interface{} {
pets := []types.Pet{}
if iteration%2 == 0 {
pets = append(pets, types.Pet{
"Flippy",
"Dolphin",
})
}
return pets
},
})
// ...
}
```### Batch
`func (factory *Factory[T]) Batch(size int, overrides ...map[string]any) []T`
Batch builds a slice of instances of a given size:
```golang
package test_somethingimport (
"testing"
"github.com/stretchr/testify/assert""github/someName/types"
"github/someName/testhelpers"
)func TestSomething(t *testing.T) {
people := testhelpers.PersonFactory.Batch(5)
assert.Len(t, people, 5)
}
```Note: You can pass to batch overrides the same as you can for build
### Create
`func (factory *Factory[T]) Create(overrides ...map[string]any) T`
If a factory defines a [Persistence Handler](#persistence-handler) you can use `.Create` to build and persist a model
instance. Create is identical to `.Build` in terms of its API.```golang
package test_somethingimport (
"testing""github/someName/testhelpers"
)func TestSomething(t *testing.T) {
person := testhelpers.PersonFactory.Create() // person is persisted using the PersistanceHandler's .Save method
// ...
}
```### CreateBatch
`func (factory *Factory[T]) CreateBatch(size int, overrides ...map[string]any) []T`
If a factory defines a [Persistence Handler](#persistence-handler) you can use `.CreateBatch` to build and persist a
slice of model instances of a given size. CreateBatch is identical to `.Batch` in terms of its API:```golang
package test_somethingimport (
"testing""github/someName/testhelpers"
)func TestSomething(t *testing.T) {
people := testhelpers.PersonFactory.CreateBatch(5) // person is persisted using the PersistanceHandler's .SaveMany method
// ...
}
```## Using Struct Tags
Fabricator uses the excellent [faker](https://github.com/bxcodec/faker) library to generate mock data. As such, you can
use the faker struct tags to control the data generation, please consult the documentation for that library to see the
available tags.## Contribution
This library is open to contributions. Please consult the [Contribution Guide](CONTRIBUTING.md).