{"id":21691381,"url":"https://github.com/followtheprocess/snapshot","last_synced_at":"2026-04-12T09:54:45.757Z","repository":{"id":263295450,"uuid":"889928953","full_name":"FollowTheProcess/snapshot","owner":"FollowTheProcess","description":"Simple, intuitive snapshot testing with Go 📸","archived":false,"fork":false,"pushed_at":"2026-04-12T07:43:16.000Z","size":566,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-04-12T09:26:16.714Z","etag":null,"topics":["go","snapshot-testing","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/FollowTheProcess.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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":"FollowTheProcess"}},"created_at":"2024-11-17T15:48:13.000Z","updated_at":"2026-04-12T07:43:11.000Z","dependencies_parsed_at":"2024-12-22T15:21:49.552Z","dependency_job_id":"f246ce42-3b46-4928-bb38-c89e22aec450","html_url":"https://github.com/FollowTheProcess/snapshot","commit_stats":null,"previous_names":["followtheprocess/snapshot"],"tags_count":15,"template":false,"template_full_name":null,"purl":"pkg:github/FollowTheProcess/snapshot","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FollowTheProcess%2Fsnapshot","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FollowTheProcess%2Fsnapshot/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FollowTheProcess%2Fsnapshot/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FollowTheProcess%2Fsnapshot/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/FollowTheProcess","download_url":"https://codeload.github.com/FollowTheProcess/snapshot/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FollowTheProcess%2Fsnapshot/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31710792,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-12T06:22:27.080Z","status":"ssl_error","status_checked_at":"2026-04-12T06:21:52.710Z","response_time":58,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: 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":["go","snapshot-testing","testing"],"created_at":"2024-11-25T17:38:05.278Z","updated_at":"2026-04-12T09:54:45.751Z","avatar_url":"https://github.com/FollowTheProcess.png","language":"Go","funding_links":["https://github.com/sponsors/FollowTheProcess"],"categories":[],"sub_categories":[],"readme":"# Snapshot\n\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"https://github.com/FollowTheProcess/snapshot/raw/main/docs/img/logo.webp\" alt=\"logo\"\u003e\n\u003c/p\u003e\n\n[![License](https://img.shields.io/github/license/FollowTheProcess/snapshot)](https://github.com/FollowTheProcess/snapshot)\n[![Go Reference](https://pkg.go.dev/badge/go.followtheprocess.codes/snapshot.svg)](https://pkg.go.dev/go.followtheprocess.codes/snapshot)\n[![Go Report Card](https://goreportcard.com/badge/github.com/FollowTheProcess/snapshot)](https://goreportcard.com/report/github.com/FollowTheProcess/snapshot)\n[![GitHub](https://img.shields.io/github/v/release/FollowTheProcess/snapshot?logo=github\u0026sort=semver)](https://github.com/FollowTheProcess/snapshot)\n[![CI](https://github.com/FollowTheProcess/snapshot/workflows/CI/badge.svg)](https://github.com/FollowTheProcess/snapshot/actions?query=workflow%3ACI)\n[![codecov](https://codecov.io/gh/FollowTheProcess/snapshot/branch/main/graph/badge.svg)](https://codecov.io/gh/FollowTheProcess/snapshot)\n\nSimple, intuitive snapshot testing for Go 📸\n\n\u003e [!WARNING]\n\u003e **snapshot is in early development and is not yet ready for use**\n\n- [Snapshot](#snapshot)\n  - [Project Description](#project-description)\n  - [Installation](#installation)\n  - [Quickstart](#quickstart)\n  - [Why use `snapshot`?](#why-use-snapshot)\n    - [📝 Consistent Serialisation](#-consistent-serialisation)\n    - [🔄 Automatic Updating](#-automatic-updating)\n    - [🗑️ Tidying Up](#️-tidying-up)\n    - [🤓 Follows Go Conventions](#-follows-go-conventions)\n  - [Filters](#filters)\n    - [Credits](#credits)\n\n## Project Description\n\nSnapshot testing is where you assert the result of your code is identical to a specific reference value... which is basically *all* testing. If you've ever written:\n\n```go\nif got != want {\n    t.Errorf(\"got %v, wanted %v\", got, want)\n}\n```\n\nThen congratulations, you've done snapshot testing 🎉 In this case `want` is the snapshot.\n\nThe trick is, when these values get large or complicated (imagine a complicated JSON document), it's difficult to manually create and maintain the snapshot every time.\n\nThe next jump up is what's typically called \"golden files\".\n\nThese are files (typically manually created) that contain the expected output, any difference in what your code produces to what's in the file is an error.\n\n**Enter snapshot testing 📸**\n\nThink of snapshot testing as an automated, configurable, and simple way of managing golden files. All you need to do is call `Snap` and everything is handled for you!\n\n## Installation\n\n```shell\ngo get go.followtheprocess.codes/snapshot@latest\n```\n\n## Quickstart\n\n```go\nimport (\n    \"testing\"\n\n    \"go.followtheprocess.codes/snapshot\"\n)\n\nfunc TestSnapshot(t *testing.T) {\n    snap := snapshot.New(t)\n\n    snap.Snap([]string{\"hello\", \"there\", \"this\", \"is\", \"a\", \"snapshot\"})\n\n    // This will store the above snapshot in testdata/snapshots/TestSnapshot.snap\n    // then all future checks will compare against this snapshot\n}\n```\n\n## Why use `snapshot`?\n\n### 📝 Consistent Serialisation\n\nBy default, a snapshot looks like this:\n\n```yaml\nsource: your_test.go\ndescription: An optional description of the snapshot\nexpression: value.Show() # The expression that generated the snapshot\n---\n\u003cyour value\u003e\n```\n\nThis format was inspired by [insta], a popular snapshot testing library in rust\n\n\u003e [!TIP]\n\u003e If you want a different format, there is also a `TextFormatter` or you can implement your own! Just implement the `snapshot.Formatter` interface and pass it in\n\u003e with the `snapshot.WithFormatter` option and you're away!\n\n### 🔄 Automatic Updating\n\nLet's say you've got a bunch of snapshots saved already, and you change your implementation. *All* those snapshots will now likely need to change (after you've carefully reviewed the changes and decided they are okay!)\n\n`snapshot` lets you do this with one line of configuration, which you can set with a test flag or environment variable, or however you like:\n\n```go\n// something_test.go\nimport (\n  \"testing\"\n\n  \"go.followtheprocess.codes/snapshot\"\n)\n\nvar update = flag.Bool(\"update\", false, \"Update snapshots automatically\")\n\nfunc TestSomething(t *testing.T) {\n  // Tell snapshot to update everything if the -update flag was used\n  snap := snapshot.New(t, snapshot.Update(*update))\n\n  // .... rest of the test\n}\n```\n\n\u003e [!TIP]\n\u003e If you declare top level flags in a test file, you can pass them to `go test`. So in this case, `go test -update` would store `true` in the update var. You can also use environments variables and test them with `os.Getenv` e.g. `UPDATE_SNAPSHOTS=true go test`. Whatever works for you.\n\n\u003e [!WARNING]\n\u003e This will update *all* snapshots in one go, so make sure you run the tests normally first and check the diffs to make sure the changes are as expected\n\n### 🗑️ Tidying Up\n\nOne criticism of snapshot testing is that if you restructure or rename your tests, you could end up with duplicated snapshots and/or messy unused ones cluttering up your repo. This is where the `Clean` option comes in:\n\n```go\n// something_test.go\nimport (\n  \"testing\"\n\n  \"go.followtheprocess.codes/snapshot\"\n)\n\nvar clean = flag.Bool(\"clean\", false, \"Clean up unused snapshots\")\n\nfunc TestSomething(t *testing.T) {\n  // Tell snapshot to prune the snapshots directory of unused snapshots\n  snap := snapshot.New(t, snapshot.Clean(*clean))\n\n  // .... rest of the test\n}\n```\n\nThis will erase all the snapshots currently managed by snapshot, and then run the tests as normal, creating the snapshots for all the new or renamed tests for the first time. The net result is a tidy snapshots directory with only what's needed\n\n### 🤓 Follows Go Conventions\n\nSnapshots are stored in a `snapshots` directory in the current package under `testdata` which is the canonical place to store test fixtures and other files of this kind, the go tool completely ignores `testdata` so you know these files will never impact your binary!\n\nSee `go help test`...\n\n```plaintext\nThe go tool will ignore a directory named \"testdata\", making it available\nto hold ancillary data needed by the tests.\n```\n\nThe files will be named automatically after the test:\n\n- Single tests will be given the name of the test e.g. `func TestMyThing(t *testing.T)` will produce a snapshot file of `testdata/snapshots/TestMyThing.snap.txt`\n- Sub tests (including table driven tests) will use the sub test name e.g. `testdata/snapshots/TestAdd/positive_numbers.snap.txt`\n\n\u003e [!TIP]\n\u003e If you want to split your snapshots with more granularity, you can name your table driven cases with a `/` in them (e.g. `\"Group/subtest name\"`) and the directory hierarchy will be created automatically for you, completely cross platform!\n\n## Filters\n\nSometimes, your snapshots might contain data that is randomly generated like UUIDs, or constantly changing like timestamps, or that might change on different platforms like filepaths, temp directory names etc.\n\nThese things make snapshot testing annoying because your snapshot changes every time or passes locally but fails in CI on a windows GitHub Actions runner (my personal bane!). `snapshot` lets you add \"filters\" to your configuration to solve this.\n\nFilters are simply regex replacements that you specify ahead of time, and if any of them appear in your snapshot, they can be replaced by whatever you want!\n\nFor example:\n\n```go\nfunc TestWithFilters(t *testing.T) {\n  // Some common ones to give you inspiration\n  snap := snapshot.New(\n    t,\n    snapshot.Filter(\"(?i)[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\", \"[UUID]\"), // Replace uuids with the literal string \"[UUID]\"\n    snapshot.Filter(`\\\\([\\w\\d]|\\.)`, \"/$1\"), // Replace windows file paths with unix equivalents\n    snapshot.Filter(`/var/folders/\\S+?/T/\\S+`, \"[TEMP_DIR]\"), // Replace a macos temp dir with the literal string \"[TEMP_DIR]\"\n  )\n}\n```\n\nBut you can imagine more:\n\n- Unix timestamps\n- Go `time.Duration` values\n- Other types of uuids, ulids etc.\n\nIf you can find a regex for it, you can filter it out!\n\n### Credits\n\nThis package was created with [copier] and the [FollowTheProcess/go-template] project template.\n\n[copier]: https://copier.readthedocs.io/en/stable/\n[FollowTheProcess/go-template]: https://github.com/FollowTheProcess/go-template\n[insta]: https://crates.io/crates/insta\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffollowtheprocess%2Fsnapshot","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffollowtheprocess%2Fsnapshot","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffollowtheprocess%2Fsnapshot/lists"}