snapshot

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Nov 20, 2024 License: MIT Imports: 12 Imported by: 0

README

Snapshot

License Go Reference Go Report Card GitHub CI codecov

Simple, intuitive snapshot testing with Go 📸

[!WARNING] snapshot is in early development and is not yet stable

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 github.com/FollowTheProcess/snapshot@latest

Quickstart

import (
    "testing"

    "github.com/FollowTheProcess/snapshot"
)

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

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

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

Why use snapshot?

📝 Total Control over Serialisation

A few other libraries are out there for snapshot testing in Go, but they typically control the serialisation for you, using a generic object dumping library. This means you get what you get and there's not much option to change it.

Not very helpful if you want your snapshots to be as readable as possible!

With snapshot, you have full control over how your type is serialised to the snapshot file (if you need it). You can either:

See Serialisation Rules 👇🏻 for more info on how snapshot decides how to snap your value

🔄 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"

  "github.com/FollowTheProcess/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

🤓 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

Serialisation Rules

snapshot deals with plain text files as snapshots, this keeps them easy to read/write for both computers and humans. But crucially, easy to diff in pull request reviews!

Because of this, it needs to know how to serialise your value (which could be basically any valid construct in Go) to plain text, so we follow a few basic rules in priority order:

  • snapshot.Snapper: If your type implements the Snapper interface, this is preferred over all other potential serialisation, this allows you to have total control over how your type is snapshotted, do whatever you like in the Snap method, just return a []byte that you'd like to look at in the snapshot and thats it!
  • json.Marshaler: If your type implements json.Marshaler, this will be used and the snapshot will be a valid JSON file (using MarshalIndent for readability)
  • encoding.TextMarshaler: If your type implements encoding.TextMarshaler, this will be used to render your value to the snapshot
  • fmt.Stringer: If your type implements the fmt.Stringer interface, this is then used instead
  • Primitive Types: Any primitive type in Go (bool, int, string etc.) is serialised according to the %v verb in the fmt package
  • Fallback: If your type hasn't been caught by any of the above rules, we will snap it using the GoString mechanism (the %#v print verb) so e.g. a []string would serialise as []string{"one", "two", "three}

[!TIP] snapshot effectively goes through this list top to bottom to discover how to serialise your type, so mechanisms at the top are preferentially chosen over mechanisms lower down. If your snapshot doesn't look quite right, consider implementing a method higher up the list to get the behaviour you need

Credits

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

Documentation

Overview

Package snapshot provides a mechanism and a simple interface for performing snapshot testing in Go tests.

Snapshots are stored under testdata, organised by test case name and may be updated automatically by passing configuration in this package.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Option

type Option func(*Shotter)

Option is a functional option for configuring snapshot tests.

func Update

func Update(v 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.

type Shotter

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

Shotter holds configuration and state and is responsible for performing the tests and managing the snapshots.

func New

func New(tb testing.TB, options ...Option) *Shotter

New builds and returns a new Shotter, applying configuration via functional options.

func (*Shotter) Path

func (s *Shotter) Path() string

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

func (*Shotter) Snap

func (s *Shotter) Snap(value any)

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

If there is no previous snapshot for this test, the current snapshot is saved and test is passed, if there is an existing snapshot and it matches the current snapshot, the test is also passed.

If the current snapshot does not match the existing one, the test will fail with a rich diff of the two snapshots for debugging.

type Snapper

type Snapper interface {
	// Snap encodes the type to bytes specifically for the purposes of comparison
	// in a snapshot test. This is where you can redact non-deterministic things or
	// simply implement a nicer format for comparison.
	Snap() ([]byte, error)
}

Snapper is an interface that lets user types control how thet are serialised to text for snapshot tests in this library.

Directories

Path Synopsis
internal
colour
Package colour implements basic text colouring for showing text diffs.
Package colour implements basic text colouring for showing text diffs.

Jump to

Keyboard shortcuts

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