{"id":16369181,"url":"https://github.com/dominicbarnes/got","last_synced_at":"2026-03-10T11:05:22.468Z","repository":{"id":57493635,"uuid":"180934919","full_name":"dominicbarnes/got","owner":"dominicbarnes","description":"A package with helpers for reducing boilerplate in tests.","archived":false,"fork":false,"pushed_at":"2025-09-12T19:25:59.000Z","size":85,"stargazers_count":7,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-09-12T21:11:55.094Z","etag":null,"topics":["golang","test-fixtures","testing"],"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/dominicbarnes.png","metadata":{"files":{"readme":"README.md","changelog":"History.md","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":"2019-04-12T05:14:11.000Z","updated_at":"2025-09-12T19:25:45.000Z","dependencies_parsed_at":"2024-06-11T18:43:20.123Z","dependency_job_id":"e61d693a-1eba-4dce-bb7e-f955f5788d5b","html_url":"https://github.com/dominicbarnes/got","commit_stats":{"total_commits":38,"total_committers":2,"mean_commits":19.0,"dds":0.02631578947368418,"last_synced_commit":"bb0a5a8fa6dee702d65486beba770a469725f268"},"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"purl":"pkg:github/dominicbarnes/got","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dominicbarnes%2Fgot","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dominicbarnes%2Fgot/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dominicbarnes%2Fgot/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dominicbarnes%2Fgot/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dominicbarnes","download_url":"https://codeload.github.com/dominicbarnes/got/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dominicbarnes%2Fgot/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30331653,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-10T05:25:20.737Z","status":"ssl_error","status_checked_at":"2026-03-10T05:25:17.430Z","response_time":106,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["golang","test-fixtures","testing"],"created_at":"2024-10-11T02:54:47.163Z","updated_at":"2026-03-10T11:05:22.445Z","avatar_url":"https://github.com/dominicbarnes.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# GoT\n\n[![GoDoc][godoc-badge]][godoc]\n\n\u003e Pronounced like \"goatee\".\n\nThis package is all about making tests easier to write and by improving clarity\nthrough removing boilerplate and code not related to test assertions.\n\nThe [Four-Phase Test][four-phase-test] paradigm heavily influences the decisions\nmade for this library.\n\n## Load: test fixtures as files (aka: testdata)\n\nOne approach to writing tests, particularly when they have non-trivial setup, is\nto use [file-based test fixtures][dave-cheney-test-fixtures].\n\nEmbedding in code is usually a suitable option for light-medium complexity code,\nbut as things grow more sophisticated, particularly for integration testing and\nfuzz testing, embedding all of that state into code gets messy, especially as\ntime passes.\n\nWhile opening up files is not difficult on it's own, there can be more to it\n(eg: decoding as JSON). Beyond dealing with single files, consider reading\ndirectories (maybe even recursively). Each new line of boilerplate like this\nincreases the noise-to-signal ratio for the test.\n\n### Working with text (string) and bytes ([]byte)\n\nThis package includes `got.Load` for loading files on disk into an annotated\nstruct to eliminate this boilerplate from your own code.\n\n```golang\npackage mypackage\n\nimport (\n  \"strings\"\n  \"testing\"\n\n  \"github.com/dominicbarnes/got/v2\"\n)\n\n// testdata/input.txt\n// hello world\n\n// testdata/expected.txt\n// HELLO WORLD\n\nfunc TestUppercase(t *testing.T) {\n  // define test cases\n  type Test struct {\n    Input    string `testdata:\"input.txt\"`\n    Expected string `testdata:\"expected.txt\"`\n  }\n\n  // load test fixtures\n  var test Test\n  got.Load(t, \"testdata\", \u0026test)\n\n  // execute the code under test\n  actual := Uppercase(test.Input)\n\n  // perform test assertions\n  if actual != test.Expected {\n    t.Fatalf(`expected \"%s\", got \"%s\"`, test.Expected, actual)\n  }\n}\n\n// code under test\nfunc Uppercase(input string) string {\n  return strings.ToUpper(input)\n}\n```\n\nWhile contrived, this demonstates a clear separation between test phases, making\nit easier to identify what the test is intending to cover.\n\nHere, simple `string` values are used, but `[]byte` could be used and it would\nbasically behave as you would expect. (raw file contents, no additional decode)\n\n### Decoding complex types (eg: struct, map, slice)\n\nTaking this to the next logical step, it is also possible for `got.Load` to\nunmarshal test fixtures into more sophisticated types (such as a map). The file\nextension maps to a codec (eg: JSON, YAML) to perform the decode.\n\n```golang\npackage mypackage\n\nimport (\n  \"reflect\"\n  \"strings\"\n  \"testing\"\n\n  \"github.com/dominicbarnes/got/v2\"\n)\n\n// testdata/input.json\n// {\n//     \"a\": \"hello\",\n//     \"b\": \"world\"\n// }\n\n// testdata/expected.json\n// {\n//     \"a\": \"HELLO\",\n//     \"b\": \"WORLD\"\n// }\n\nfunc TestUppercaseMap(t *testing.T) {\n  // define test cases\n  type Test struct {\n    Input    map[string]string `testdata:\"input.json\"`\n    Expected map[string]string `testdata:\"expected.json\"`\n  }\n\n  // load test fixtures\n  var test Test\n  got.LoadTestData(t, \"testdata\", \u0026test)\n\n  // execute the code under test\n  actual := UppercaseMap(test.Input)\n\n  // perform test assertions\n  if !reflect.DeepEqual(actual, test.Expected) {\n    t.Fatalf(`expected \"%+v\", got \"%+v\"`, test.Expected, actual)\n  }\n}\n\n// code under test\nfunc UppercaseMap(input map[string]string) map[string]string {\n  output := make(map[string]string)\n  for k, v := range input {\n    output[k] = strings.ToUpper(v)\n  }\n  return output\n}\n```\n\nOut of the box, this library supports decoding JSON (`.json`) and YAML (`.yml`,\n`.yaml`). You can define your own codecs or override the defaults using\n`got/codec.Register`.\n\n### Working with dynamic maps of files (explode)\n\nWhen testing a component that can produce outputs dynamically, or even if just\nhaving a single file for the entire output is undesirable, a `map` type can be\nused with the `explode` struct tag option to map to multiple files with a glob.\n\n```golang\npackage mypackage\n\nimport (\n  \"reflect\"\n  \"strings\"\n  \"testing\"\n\n  \"github.com/dominicbarnes/got/v2\"\n)\n\n// testdata/input.json\n// {\n//   \"a\": \"hello\",\n//   \"b\": \"world\"\n// }\n\n// testdata/expected/a.txt\n// HELLO\n\n// testdata/expected/a.txt\n// WORLD\n\nfunc TestUppercaseMap(t *testing.T) {\n  // define test cases\n  type Test struct {\n    Input    map[string]string `testdata:\"input.json\"`\n    Expected map[string]string `testdata:\"expected/*.txt,explode\"`\n  }\n\n  // load test fixtures\n  var test Test\n  got.LoadTestData(t, \"testdata\", \u0026test)\n\n  // execute the code under test\n  actual := UppercaseAsFiles(test.Input)\n\n  // perform test assertions\n  if !reflect.DeepEqual(actual, test.Expected) {\n    t.Fatalf(`expected \"%+v\", got \"%+v\"`, test.Expected, actual)\n  }\n}\n\n// code under test\nfunc UppercaseAsFiles(input map[string]string) map[string]string {\n  output := make(map[string]string)\n  for k, v := range input {\n    output[fmt.Sprintf(\"expected/%s.txt\", k)] = strings.ToUpper(v)\n  }\n  return output\n}\n```\n\nNotice that the `testdata` struct tag uses a glob pattern along with the\n`explode` option.\n\nThe `Input` map (**not** using `explode`) will look like:\n\n```golang\nmap[string]string{\n  \"a\": \"hello\",\n  \"b\": \"world\",\n}\n```\n\nThe `Expected` map (using `explode`) will look like:\n\n```golang\nmap[string]string{\n  \"expected/a.txt\": \"HELLO\",\n  \"expected/b.txt\": \"WORLD\",\n}\n```\n\n## Suite: Directory-driven test cases\n\nConsider testing a component with medium-high complexity. Breaking out each case\ninto manually-defined test functions is workable, but becomes repetitive if the\ntest setup is always identical.\n\nOne approach would be to leverage [table-driven tests][table-driven-tests] to\nperform that identical setup within a loop. GoT provides another approach, which\ntargets a directory and treats each sub-directory there as a separate test case.\n\n```golang\npackage mypackage\n\nimport (\n  \"strings\"\n  \"testing\"\n\n  \"github.com/dominicbarnes/got/v2\"\n)\n\n// testdata/hello-world/input.txt\n// hello world\n\n// testdata/hello-world/expected.txt\n// HELLO WORLD\n\n\n// testdata/foo-bar/input.txt\n// foo bar\n\n// testdata/foo-bar/expected.txt\n// FOO BAR\n\n\nfunc TestUppercaseSuite(t *testing.T) {\n  // define test cases\n  type Test struct {\n    Input    string `testdata:\"input.txt\"`\n    Expected string `testdata:\"expected.txt\"`\n  }\n\n  // define test suite\n  suite := got.TestSuite{\n    Dir: \"testdata\",\n    TestFunc: func (t *testing.T, c got.TestCase) {\n      // load test fixtures\n      var test Test\n      c.Load(t, \u0026test)\n\n      // execute the code under test\n      actual := Uppercase(test.Input)\n\n      // perform test assertions\n      if actual != test.Expected {\n        t.Fatalf(`expected \"%s\", got \"%s\"`, test.Expected, actual)\n      }\n    },\n  }\n\n  // run the test suite: \"hello-world\" and \"foo-bar\" each get a sub-test\n  suite.Run(t)\n}\n\n// code under test\nfunc Uppercase(input string) string {\n  return strings.ToUpper(input)\n}\n```\n\n### Skipping test cases\n\nSometimes, a test case needs to be disabled temporarily, but deleting it\naltogether may not be desirable. To accomplish this, simply rename the directory\nto have a \".skip\" suffix.\n\nAlternatively, if skipping all but specific tests is desired, add a \".only\"\nsuffix to skip all other test cases.\n\n\n## Assert: using and updating golden files\n\nIn Golang, [golden files][golden-files] are generated when your code is known to\nbe working as intended, then saved and referenced later to ensure that outputs\ndo not changed unexpectedly. This is very useful when outputs are difficult to\ndefined by hand (eg: binary data) or are just large (eg: ETL testing).\n\n`got.Assert` is the companion to `got.Load` in that it takes an annotated struct\nbut is more focused on writing the data to disk rather than reading it, creating\nthese \"golden files\". There are 2 modes of operation here, determined by the\n`test.update-golden` flag.\n\nBy default, `got.Assert` will compare the input to what already exists on disk,\nfailing the test if they do not match. When `go test -update-golden` is used,\nthe input will simply be written to disk, skipping the assertion altogether.\n\n\n```golang\npackage mypackage\n\nimport (\n  \"strings\"\n  \"testing\"\n\n  \"github.com/dominicbarnes/got/v2\"\n)\n\n// NOTE: no expected.txt files are defined\n\n// testdata/hello-world/input.txt\n// hello world\n\n// testdata/foo-bar/input.txt\n// foo bar\n\nfunc TestUppercaseAssert(t *testing.T) {\n  // define test inputs\n  type Test struct {\n    Input string `testdata:\"input.txt\"`\n  }\n\n  // define test expectations\n  type Expected struct {\n    Output string `testdata:\"expected.txt\"`\n  }\n\n  // define test suite\n  suite := got.TestSuite{\n    Dir: \"testdata\",\n    TestFunc: func (t *testing.T, c got.TestCase) {\n      // load test fixtures\n      var test Test\n      c.Load(t, \u0026test)\n\n      // execute the code under test\n      actual := Uppercase(test.Input)\n\n      // perform test assertions\n      // 1. tests will fail as expected.txt files are missing (FAIL)\n      // 2. add -update-golden and expected.txt files will be written (PASS)\n      // 3. tests will pass as long as outputs don't change (PASS)\n      got.Assert(\u0026Expected{Output: actual})\n    },\n  }\n\n  // run the test suite\n  suite.Run(t)\n}\n\n// code under test\nfunc Uppercase(input string) string {\n  return strings.ToUpper(input)\n}\n```\n\n## RunTestSuite: putting it all together\n\nUsing the `RunTestSuite` helper function combines basically every feature above\ninto an easy-to-grok function call for straightforward test suites.\n\nIt uses type parameters (aka: generics) to accept a function with 2 parameters:\n`*testing.T` and a test configuration struct (conventionally named `Test`) and\nthen returning a test assertions struct (conventionally named `Expected`).\n\nThe `Test` struct is passed to `Load` automatically and the returned `Expected`\nis passed to `Assert` automatically.\n\n```golang\npackage mypackage\n\nimport (\n  \"strings\"\n  \"testing\"\n\n  \"github.com/dominicbarnes/got/v2\"\n)\n\n// testdata/hello-world/input.txt\n// hello world\n\n// testdata/hello-world/expected.txt\n// HELLO WORLD\n\n// testdata/foo-bar/input.txt\n// foo bar\n\n// testdata/foo-bar/expected.txt\n// FOO BAR\n\nfunc TestUppercase(t *testing.T) {\n  // define test inputs\n  type Test struct {\n    Input string `testdata:\"input.txt\"`\n  }\n\n  // define test expectations\n  type Expected struct {\n    Output string `testdata:\"expected.txt\"`\n  }\n\n  got.RunTestSuite(t, \"testdata\", func (t *testing.T, tc got.TestCase, test Test) Expected {\n    // execute the code under test\n    actual := Uppercase(test.Input)\n\n    // return the actual output for assertions \n    return Expected{Output: actual}\n  })\n}\n\n// code under test\nfunc Uppercase(input string) string {\n  return strings.ToUpper(input)\n}\n```\n\nWhile contrived, the boilerplate for things like `Load` and `Assert` being\nremoved really puts the focus on the test itself as much as possible, which is\neven more obvious for more sophisticated tests.\n\nCheck out [godoc][godoc] for more information about the API.\n\n[dave-cheney-test-fixtures]: https://dave.cheney.net/2016/05/10/test-fixtures-in-\n[four-phase-test]: http://xunitpatterns.com/Four%20Phase%20Test.html\n[golden-files]: https://ieftimov.com/post/testing-in-go-golden-files/\n[table-driven-tests]: https://dave.cheney.net/2019/05/07/prefer-table-driven-tests\n[godoc]: https://godoc.org/github.com/dominicbarnes/got\n[godoc-badge]: https://godoc.org/github.com/dominicbarnes/got?status.svg","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdominicbarnes%2Fgot","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdominicbarnes%2Fgot","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdominicbarnes%2Fgot/lists"}