{"id":13714182,"url":"https://github.com/k-yomo/fixtory","last_synced_at":"2026-04-02T02:38:22.259Z","repository":{"id":38208874,"uuid":"286068312","full_name":"k-yomo/fixtory","owner":"k-yomo","description":"Type-safe, DRY, flexible test fixture factory based on Go 1.18+ Generics","archived":false,"fork":false,"pushed_at":"2024-11-14T19:08:09.000Z","size":108,"stargazers_count":98,"open_issues_count":5,"forks_count":5,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-30T06:07:09.520Z","etag":null,"topics":["factory-bot","factory-boy","fixture-factory","fixture-replacement","generics","go","golang"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/k-yomo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-08-08T15:23:10.000Z","updated_at":"2025-02-17T18:28:01.000Z","dependencies_parsed_at":"2024-11-24T22:02:55.942Z","dependency_job_id":"3175f52a-fec5-47ed-88cb-fe6e66a192bf","html_url":"https://github.com/k-yomo/fixtory","commit_stats":null,"previous_names":[],"tags_count":15,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/k-yomo%2Ffixtory","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/k-yomo%2Ffixtory/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/k-yomo%2Ffixtory/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/k-yomo%2Ffixtory/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/k-yomo","download_url":"https://codeload.github.com/k-yomo/fixtory/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247445680,"owners_count":20939961,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["factory-bot","factory-boy","fixture-factory","fixture-replacement","generics","go","golang"],"created_at":"2024-08-02T23:01:54.179Z","updated_at":"2026-04-02T02:38:22.213Z","avatar_url":"https://github.com/k-yomo.png","language":"Go","funding_links":[],"categories":["Repositories"],"sub_categories":[],"readme":"[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/k-yomo/fixtory/blob/master/LICENSE)\n[![Main Workflow](https://github.com/k-yomo/fixtory/workflows/Main%20Workflow/badge.svg)](https://github.com/k-yomo/fixtory/actions?query=workflow%3A%22Main+Workflow%22)\n[![codecov](https://codecov.io/gh/k-yomo/fixtory/branch/master/graph/badge.svg)](https://codecov.io/gh/k-yomo/fixtory)\n[![Go Report Card](https://goreportcard.com/badge/github.com/k-yomo/fixtory)](https://goreportcard.com/report/github.com/k-yomo/fixtory)\n\nFixtory is a test fixture factory which initializes type-safe, DRY, flexible fixtures with the power of Generics.\n\nBy using Fixtory...\n- No more redundant repeated field value setting\n- No more type assertion to convert from interface\n- No more error handling when building fixture\n- No more exhaustion to just prepare test data\n\n## Installation\n```sh\n$ go get github.com/k-yomo/fixtory/v2\n```\n\n## Getting started\nComplete code is in [example](example).\n\nUse factory to initialize fixtures\n```go\n// Author represents article's author\ntype Author struct {\n    ID   int\n    Name string\n}\n\n// Article represents article\ntype Article struct {\n    ID                 int\n    Title              string\n    Body               string\n    AuthorID           int\n    PublishScheduledAt time.Time\n    PublishedAt        time.Time\n    Status             ArticleStatus\n    LikeCount          int\n}\n\nvar authorBluePrint = func(i int, last Author) Author {\n\tnum := i + 1\n\treturn Author{\n\t\tID:   num,\n\t\tName: fmt.Sprintf(\"Author %d\", num),\n\t}\n}\n\nvar articleBluePrint = func(i int, last Article) Article {\n\tnum := i + 1\n\treturn Article{\n\t\tID:                 num,\n\t\tTitle:              fmt.Sprintf(\"Article %d\", i+1),\n\t\tAuthorID:           num,\n\t\tPublishScheduledAt: time.Now().Add(-1 * time.Hour),\n\t\tPublishedAt:        time.Now().Add(-1 * time.Hour),\n\t\tLikeCount:          15,\n\t}\n}\n\n\nfunc TestArticleList_SelectAuthoredBy(t *testing.T) {\n    authorFactory := fixtory.NewFactory(t, Author{})\n    articleFactory := fixtory.NewFactory(t, Article{})\n\n    author1, author2 := authorFactory.NewBuilder(authorBluePrint).Build2()\n    articlesAuthoredBy1 := articleFactory.NewBuilder(articleBluePrint, Article{AuthorID: author1.ID}).BuildList(4)\n    articleAuthoredBy2 := articleFactory.NewBuilder(articleBluePrint, Article{AuthorID: author2.ID}).Build()\n\n    type args struct {\n        authorID int\n    }\n    tests := []struct {\n        name string\n        list ArticleList\n        args args\n        want ArticleList\n    }{\n        {\n            name: \"returns articles authored by author 1\",\n            list: append(articlesAuthoredBy1, articleAuthoredBy2),\n            args: args{authorID: author1.ID},\n            want: articlesAuthoredBy1,\n        },\n        {\n            name: \"returns articles authored by author 2\",\n            list: append(articlesAuthoredBy1, articleAuthoredBy2),\n            args: args{authorID: author2.ID},\n            want: ArticleList{articleAuthoredBy2},\n        },\n    }\n    for _, tt := range tests {\n        t.Run(tt.name, func(t *testing.T) {\n            if got := tt.list.SelectAuthoredBy(tt.args.authorID); !reflect.DeepEqual(got, tt.want) {\n                t.Errorf(\"SelectAuthoredBy() = %v, want %v\", got, tt.want)\n            }\n        })\n    }\n}\n```\n\n## How it works\nThere are 4 layers in the process of initializing fixture in fixtory. \nThe layers are stacked and each one is a delta of the changes from the previous layer like Dockerfile.\n\n### 1. Blueprint\nBlueprint is the base of fixture(like FROM in Dockerfile) and called first.\nYou need to implement blueprint function to meet generated blueprint type (like below)\nIt should return instance with generic field values.\n```\ntype TestArticleBluePrintFunc func(i int, last Article) Article\n```\n\n### 2. Traits\nTo overwrite some fields, you can use traits.\nTraits are applied in the order of arguments to all fixtures.\n※ Only non-zero value will be set.\n```go\n//  Repeatedly used trait would be better to define as global variable.\nvar articleTraitPublished = Article{\n\tStatus:             ArticleStatusOpen,\n\tPublishScheduledAt: time.Now().Add(-1 * time.Hour),\n\tPublishedAt:        time.Now().Add(-1 * time.Hour),\n\tLikeCount:          15,\n}\n\n// recently published articles\narticles:= articleFactory.NewBuilder(\n               nil, \n               articleTraitPublished,\n               Article{AuthorID: 5, PublishedAt: time.Now().Add(-1 * time.Minute)},\n           ).BuildList(2)\n```\n\n### 3. Each Param\nWhen you want to overwrite a specific fixture in the list, use EachParam.\nEach Param overwrites the same index struct as parameter.\n※ Only non-zero value will be set.\n```go\narticleFactory := NewArticleFactory(t)\narticles := articleFactory.NewBuilder(nil, Article{Title: \"test article\"})\n                .EachParam(Article{AuthorID: 1}, Article{AuthorID: 2}, Article{AuthorID: 2})\n                .BuildList(3)\n```\n\n### 4. Zero\nSince there is no way to distinguish default zero value or intentionally set zero in params,\nyou can overwrite fields with zero value like below, and it will be applied at the last minute.\n```go\narticleFactory := NewArticleFactory(t)\n// AuthorID will be overwritten with zero value.\narticles := articleFactory.NewBuilder(articleBluePrint, Article{AuthorID: author1.ID}).\n                Zero(ArticleAuthorIDField).\n                BuildList(4)\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fk-yomo%2Ffixtory","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fk-yomo%2Ffixtory","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fk-yomo%2Ffixtory/lists"}