snapshot

package module
v0.9.1 Latest Latest
Warning

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

Go to latest
Published: Nov 22, 2025 License: MIT Imports: 13 Imported by: 0

README

Snapshot

logo

License Go Reference Go Report Card GitHub CI codecov

Simple, intuitive snapshot testing for Go 📸

[!WARNING] snapshot is in early development and is not yet ready for use

Project Description

Snapshot 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:

if got != want {
    t.Errorf("got %v, wanted %v", got, want)
}

Then congratulations, you've done snapshot testing 🎉 In this case want is the snapshot.

The 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.

The next jump up is what's typically called "golden files".

These 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.

Enter snapshot testing 📸

Think 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!

Installation

go get go.followtheprocess.codes/snapshot@latest

Quickstart

import (
    "testing"

    "go.followtheprocess.codes/snapshot"
)

func TestSnapshot(t *testing.T) {
    snap := snapshot.New(t)

    snap.Snap([]string{"hello", "there", "this", "is", "a", "snapshot"})

    // This will store the above snapshot in testdata/snapshots/TestSnapshot.snap
    // then all future checks will compare against this snapshot
}

Why use snapshot?

📝 Consistent Serialisation

By default, a snapshot looks like this:

source: your_test.go
description: An optional description of the snapshot
expression: value.Show() # The expression that generated the snapshot
---
<your value>

This format was inspired by insta, a popular snapshot testing library in rust

[!TIP] 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 with the snapshot.WithFormatter option and you're away!

🔄 Automatic Updating

Let'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!)

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:

// something_test.go
import (
  "testing"

  "go.followtheprocess.codes/snapshot"
)

var update = flag.Bool("update", false, "Update snapshots automatically")

func TestSomething(t *testing.T) {
  // Tell snapshot to update everything if the -update flag was used
  snap := snapshot.New(t, snapshot.Update(*update))

  // .... rest of the test
}

[!TIP] 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.

[!WARNING] 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

🗑️ Tidying Up

One 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:

// something_test.go
import (
  "testing"

  "go.followtheprocess.codes/snapshot"
)

var clean = flag.Bool("clean", false, "Clean up unused snapshots")

func TestSomething(t *testing.T) {
  // Tell snapshot to prune the snapshots directory of unused snapshots
  snap := snapshot.New(t, snapshot.Clean(*clean))

  // .... rest of the test
}

This 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

🤓 Follows Go Conventions

Snapshots 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!

See go help test...

The go tool will ignore a directory named "testdata", making it available
to hold ancillary data needed by the tests.

The files will be named automatically after the test:

  • 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
  • Sub tests (including table driven tests) will use the sub test name e.g. testdata/snapshots/TestAdd/positive_numbers.snap.txt

[!TIP] 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!

Filters

Sometimes, 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.

These 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.

Filters 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!

For example:

func TestWithFilters(t *testing.T) {
  // Some common ones to give you inspiration
  snap := snapshot.New(
    t,
    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]"
    snapshot.Filter(`\\([\w\d]|\.)`, "/$1"), // Replace windows file paths with unix equivalents
    snapshot.Filter(`/var/folders/\S+?/T/\S+`, "[TEMP_DIR]"), // Replace a macos temp dir with the literal string "[TEMP_DIR]"
  )
}

But you can imagine more:

  • Unix timestamps
  • Go time.Duration values
  • Other types of uuids, ulids etc.

If you can find a regex for it, you can filter it out!

Credits

This package was created with copier and the FollowTheProcess/go-template project template.

Documentation

Overview

Package snapshot is a Snapshot testing library for Go.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Formatter added in v0.7.0

type Formatter interface {
	// Format returns a formatted version of 'value' as a raw byte slice, these
	// bytes are interpreted as the snapshot and will be written and read from disk
	// during snapshot comparisons.
	Format(value any) ([]byte, error)

	// Ext returns the file extension for the snapshot, including the dot
	// e.g. ".custom".
	Ext() string
}

Formatter is an interface describing something capable of producing a snapshot.

func InstaFormatter added in v0.8.0

func InstaFormatter(description string) Formatter

InstaFormatter returns a Formatter that produces snapshots in the insta yaml format.

It takes a description for the snapshot.

func TextFormatter added in v0.8.0

func TextFormatter() Formatter

TextFormatter returns a Formatter that produces snapshots by simply dumping the value as plain text.

type Option

type Option func(*Runner) error

Option is a functional option for configuring a snapshot test Runner.

func Clean

func Clean(clean bool) Option

Clean is an Option that tells snapshot to erase the snapshots directory for the given test before it runs. This is particularly useful if you've renamed or restructured your tests since the snapshots were last generated to remove all unused snapshots.

Typically, you'll want the value of this option to be set from an environment variable or a test flag so that it only happens when explicitly requested, as like Update, fresh snapshots will always pass the tests.

func Color

func Color(enabled bool) Option

Color is an Option that tells snapshot whether it can use ANSI terminal colors when rendering the diff.

By default snapshot will auto-detect whether to use colour based on things like $NO_COLOR, $FORCE_COLOR, whether os.Stdout is a terminal etc.

Passing this option will override default detection and set the provided value.

func Description added in v0.7.0

func Description(description string) Option

Description is an Option that attaches a brief, human-readable description that may be serialised with the snapshot depending on the format.

func Filter

func Filter(pattern, replacement string) Option

Filter is an Option that configures a filter that is applied to a snapshot prior to saving to disc.

Filters can be used to ensure deterministic snapshots given non-deterministic data.

A motivating example would be if your snapshot contained a UUID that was generated each time, your snapshot would always fail.

Instead you might add a filter:

snapshot.New(t, snapshot.Filter("(?i)[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}", "[UUID]"))

Now any match of this regex anywhere in your snapshot will be replaced by the literal string "[UUID]".

Inside replacement, '$' may be used to refer to capture groups. For example:

snapshot.Filter(`\\([\w\d]|\.)`, "/$1")

Replaces windows style paths with a unix style path with the same information, e.g.

some\windows\path.txt

Becomes:

some/windows/path.txt

Filters use regexp.ReplaceAll underneath so in general the behaviour is as documented there, see also regexp.Expand for documentation on how '$' may be used.

func Update

func Update(update bool) Option

Update is an Option that tells snapshot whether to automatically update the stored snapshots with the new value from each test.

Typically, you'll want the value of this option to be set from an environment variable or a test flag so that you can inspect the diffs prior to deciding that the changes are expected, and therefore the snapshots should be updated.

func WithFormatter added in v0.7.0

func WithFormatter(formatter Formatter) Option

WithFormatter sets the Formatter that snapshots will be serialised and deserialised with.

The default implementation of a Formatter is one that serialises snapshots in the insta yaml format.

In the future we may provide alternative formatters, but in the meantime you can implement your own Formatter and pass it here.

type Runner added in v0.7.0

type Runner struct {
	// contains filtered or unexported fields
}

Runner is the snapshot testing runner.

It holds configuration and state for the snapshot test in question.

func New

func New(tb testing.TB, options ...Option) Runner

New initialises a new snapshot test Runner.

The behaviour of the snapshot test can be configured by passing a number of Option.

func (Runner) Path added in v0.7.0

func (r Runner) Path() string

Path returns the path that a snapshot would be saved at for any given test.

func (Runner) Snap added in v0.7.0

func (r Runner) Snap(value any)

Snap takes a snapshot of a value and compares it against the previous snapshot stored under testdata/snapshots using the name of the test as the filepath.

If there is a previous snapshot saved for this test, the newly generated snapshot is compared with the one on disk. If the two snapshots differ, the test is failed and a rich diff is shown for comparison.

If the newly generated snapshot and the one previously saved are the same, the test passes.

Likewise if there was no previous snapshot, the new one is written to disk and the test passes.

Directories

Path Synopsis
internal
format
Package format provides a variety of snapshot serialisation formats.
Package format provides a variety of snapshot serialisation formats.
format/insta
Package insta implements the .snap format for snapshots, popularised by the rust insta crate.
Package insta implements the .snap format for snapshots, popularised by the rust insta crate.
format/text
Package text provides a simple text formatter for snapshots.
Package text provides a simple text formatter for snapshots.

Jump to

Keyboard shortcuts

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