diff --git a/README.md b/README.md index 3fe07f6..e1618a7 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,7 @@ type Person struct { } ``` -Since factories can be reused, its a good idea to define them in a specific package from which they can be imported. In +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 @@ -114,20 +114,173 @@ import ( "github/someName/types" ) -var PersonFactory = fabricator.New[types.Person](types.Person{}, fabricator.Options[Person]{ +var PersonFactory = fabricator.New[types.Person](types.Person{}, fabricator.Options[types.Person]{ Defaults: map[string]any{ "FirstName": "Moishe", "LastName": "Zuchmir", - "Pets": []types.Pet{ - { - "Flippy", - "Dolphin", - }, + "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 fabricator + +type 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 testhelpers + +import ( + "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 -Build is the +`func (factory *Factory[T]) Build(overrides ...map[string]any) T` + +Build creates a single instance of the factory's model: + +```golang +package test_something + +import ( + "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_something + +import ( + "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_something + +import ( + "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. + +### 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. + +## Contribution + +This library is open to contributions. Please consult the [Contribution Guide](CONTRIBUTING.md).