fixtory

package module
v0.1.2 Latest Latest
Warning

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

Go to latest
Published: Oct 20, 2021 License: MIT Imports: 10 Imported by: 0

README

Fixtory Logo Main Workflow codecov Go Report Card

Fixtory is a test fixture factory generator which generates type-safe, DRY, flexible fixture factory.

By using Fixtory...

  • No more redundant repeated field value setting
  • No more type assertion to convert from interface
  • No more error handling when building fixture
  • No more exhaustion to just prepare test data

Installation

$ go get github.com/k-yomo/fixtory/cmd/fixtory

Usage

Usage of fixtory:
        fixtory [flags] -type T [directory]
Flags:
  -output string
        output file name; default srcdir/fixtory_gen.go
  -package string
        package name; default same package as the type
  -type string
        comma-separated list of type names; must be set

Getting started

Complete code is in example.

  1. Add go:generate comment to generate factories
//go:generate fixtory -type=Author,Article -output=article.fixtory.go

// Author represents article's author
type Author struct {
	ID   int
	Name string
}

// Article represents article
type Article struct {
	ID                 int
	Title              string
	Body               string
	AuthorID           int
	PublishScheduledAt time.Time
	PublishedAt        time.Time
	Status             ArticleStatus
	LikeCount          int
}
  1. Generate fixture factories
$ go generate ./...
  1. Use factory to initialize fixtures
var authorBluePrint = func(i int, last Author) Author {
	num := i + 1
	return Author{
		ID:   num,
		Name: fmt.Sprintf("Author %d", num),
	}
}

var articleBluePrint = func(i int, last Article) Article {
	num := i + 1
	return Article{
		ID:                 num,
		Title:              fmt.Sprintf("Article %d", i+1),
		AuthorID:           num,
		PublishScheduledAt: time.Now().Add(-1 * time.Hour),
		PublishedAt:        time.Now().Add(-1 * time.Hour),
		LikeCount:          15,
	}
}

func TestArticleList_SelectAuthoredBy(t *testing.T) {
	authorFactory := NewAuthorFactory(t)
	articleFactory := NewArticleFactory(t)

	author1, author2 := authorFactory.NewBuilder(authorBluePrint).Build2()
	articlesAuthoredBy1 := articleFactory.NewBuilder(articleBluePrint, Article{AuthorID: author1.ID}).BuildList(4)
	articleAuthoredBy2 := articleFactory.NewBuilder(articleBluePrint, Article{AuthorID: author2.ID}).Build()

	type args struct {
		authorID int
	}
	tests := []struct {
		name string
		list ArticleList
		args args
		want ArticleList
	}{
		{
			name: "returns articles authored by author 1",
			list: append(articlesAuthoredBy1, articleAuthoredBy2),
			args: args{authorID: author1.ID},
			want: articlesAuthoredBy1,
		},
		{
			name: "returns articles authored by author 2",
			list: append(articlesAuthoredBy1, articleAuthoredBy2),
			args: args{authorID: author2.ID},
			want: ArticleList{articleAuthoredBy2},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := tt.list.SelectAuthoredBy(tt.args.authorID); !reflect.DeepEqual(got, tt.want) {
				t.Errorf("SelectAuthoredBy() = %v, want %v", got, tt.want)
			}
		})
	}
}

How it works

There are 4 layers in the process of initializing fixture in fixtory. The layers are stacked and each one is a delta of the changes from the previous layer like Dockerfile.

1. Blueprint

Blueprint is the base of fixture(like FROM in Dockerfile) and called first. You need to implement blueprint function to meet generated blueprint type (like below) It should return instance with generic field values.

type TestArticleBluePrintFunc func(i int, last Article) Article

2. Traits

To overwrite some fields, you can use traits. Traits are applied in the order of arguments to all fixtures. ※ Only non-zero value will be set.

//  Repeatedly used trait would be better to define as global variable.
var articleTraitPublished = Article{
	Status:             ArticleStatusOpen,
	PublishScheduledAt: time.Now().Add(-1 * time.Hour),
	PublishedAt:        time.Now().Add(-1 * time.Hour),
	LikeCount:          15,
}

// recently published articles
articles:= articleFactory.NewBuilder(
               nil, 
               articleTraitPublished,
               Article{AuthorID: 5, PublishedAt: time.Now().Add(-1 * time.Minute)},
           ).BuildList(2)

3. Each Param

When you want to overwrite a specific fixture in the list, use EachParam. Each Param overwrites the same index struct as parameter. ※ Only non-zero value will be set.

articleFactory := NewArticleFactory(t)
articles := articleFactory.NewBuilder(nil, Article{Title: "test article"})
                .EachParam(Article{AuthorID: 1}, Article{AuthorID: 2}, Article{AuthorID: 2})
                .BuildList(3)

4. Zero

Since there is no way to distinguish default zero value or intentionally set zero in params, you can overwrite fields with zero value like below, and it will be applied at the last minute.

articleFactory := NewArticleFactory(t)
// AuthorID will be overwritten with zero value.
articles := articleFactory.NewBuilder(articleBluePrint, Article{AuthorID: author1.ID}).
                Zero(ArticleAuthorIDField).
                BuildList(4)

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ConvertToInterfaceArray

func ConvertToInterfaceArray(from interface{}) []interface{}

ConvertToInterfaceArray converts any type of array to interface array

func Generate

func Generate(targetDir string, outputDir string, types []string, pkgName string, newWriter func() (writer io.Writer, close func(), err error)) error

func MapNotZeroFields

func MapNotZeroFields(from interface{}, to interface{})

MapNotZeroFields maps a struct fields to another struct fields if the field's value is not zero from and to must be same struct, and to must be pointer

Types

type BluePrintFunc

type BluePrintFunc func(i int, last interface{}) interface{}

type Builder

type Builder struct {
	*Factory
	// contains filtered or unexported fields
}

func (*Builder) Build

func (b *Builder) Build() interface{}

func (*Builder) BuildList

func (b *Builder) BuildList(n int) []interface{}

func (*Builder) EachParam added in v0.0.4

func (b *Builder) EachParam(params ...interface{}) *Builder

func (*Builder) ResetAfter added in v0.0.4

func (b *Builder) ResetAfter() *Builder

func (*Builder) Zero added in v0.0.4

func (b *Builder) Zero(fields ...string) *Builder

type Factory

type Factory struct {

	// v is a pointer to struct
	OnBuild func(t *testing.T, v interface{})
	// contains filtered or unexported fields
}

func NewFactory

func NewFactory(t *testing.T, v interface{}) *Factory

func (*Factory) NewBuilder

func (f *Factory) NewBuilder(bluePrint BluePrintFunc, traits ...interface{}) *Builder

func (*Factory) Reset

func (f *Factory) Reset()

Directories

Path Synopsis
cmd
pkg

Jump to

Keyboard shortcuts

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