Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up

Jest-like snapshot testing in Go 📸

License

NotificationsYou must be signed in to change notification settings

gkampitakis/go-snaps

Repository files navigation

GoGo Report CardGo Reference

Jest-like snapshot testing in Go


Logo

Contents

Installation

To installgo-snaps, usego get:

go get github.com/gkampitakis/go-snaps

Import thego-snaps/snaps package into your code:

package exampleimport ("testing""github.com/gkampitakis/go-snaps/snaps")funcTestExample(t*testing.T) {snaps.MatchSnapshot(t,"Hello World")}

MatchSnapshot

MatchSnapshot can be used to capture any type of data structured or unstructured.

You can pass multiple parameters toMatchSnapshot or callMatchSnapshot multipletimes inside the same test. The difference is in the latter, it willcreate multiple entries in the snapshot file.

// test_simple.gofuncTestSimple(t*testing.T) {t.Run("should make multiple entries in snapshot",func(t*testing.T) {snaps.MatchSnapshot(t,5,10,20,25)snaps.MatchSnapshot(t,"some value")  })}

go-snaps saves the snapshots in__snapshots__ directory and the filename is the test file name with extension.snap.

So for example if your test is calledtest_simple.go when you run your tests, a snapshot filewill be created at./__snapshots__/test_simple.snaps.

MatchStandaloneSnapshot

MatchStandaloneSnapshot will create snapshots on separate files as opposed toMatchSnapshot which adds multiple snapshots inside the same file.

Combined withsnaps.Ext you can have proper syntax highlighting and better readability

// test_simple.gofuncTestSimple(t*testing.T) {snaps.MatchStandaloneSnapshot(t,"Hello World")// or create an html snapshot filesnaps.WithConfig(snaps.Ext(".html")).MatchStandaloneSnapshot(t,"<html><body><h1>Hello World</h1></body></html>")}

go-snaps saves the snapshots in__snapshots__ directory and the filename is thet.Name() plus a number plus the extension.snap.

So for the above example the snapshot file name will be./__snapshots__/TestSimple_1.snap and./__snapshots__/TestSimple_1.snap.html.

MatchJSON

MatchJSON can be used to capture data that can represent a valid json.

You can pass a valid json in form ofstring or[]byte or whatever value can be passedsuccessfully onjson.Marshal.

funcTestJSON(t*testing.T) {typeUserstruct {AgeintEmailstring  }snaps.MatchJSON(t,`{"user":"mock-user","age":10,"email":"mock@email.com"}`)snaps.MatchJSON(t, []byte(`{"user":"mock-user","age":10,"email":"mock@email.com"}`))snaps.MatchJSON(t,User{10,"mock-email"})}

JSON will be saved in snapshot in pretty format for more readability and deterministic diffs.

MatchStandaloneJSON

MatchStandaloneJSON will create snapshots on separate files as opposed toMatchJSON which adds multiple snapshots inside the same file.

funcTestSimple(t*testing.T) {snaps.MatchStandaloneJSON(t,`{"user":"mock-user","age":10,"email":"mock@email.com"}`)snaps.MatchStandaloneJSON(t,User{10,"mock-email"})}

go-snaps saves the snapshots in__snapshots__ directory and the filename is thet.Name() plus a number plus the extension.snap.json.

So for the above example the snapshot file name will be./__snapshots__/TestSimple_1.snap.json and./__snapshots__/TestSimple_2.snap.json.

MatchYAML

MatchYAML can be used to capture data that can represent a valid yaml.

You can pass a valid json in form ofstring or[]byte or whatever value can be passedsuccessfully onyaml.Marshal.

funcTestYAML(t*testing.T) {typeUserstruct {AgeintEmailstring  }snaps.MatchYAML(t,"user:\"mock-user\"\nage: 10\nemail: mock@email.com")snaps.MatchYAML(t, []byte("user:\"mock-user\"\nage: 10\nemail: mock@email.com"))snaps.MatchYAML(t,User{10,"mock-email"})}

Matchers

MatchJSON's andMatchYAML's third argument can accept a list of matchers. Matchers are functions that can actas property matchers and test values.

You can pass the path of the property you want to match and test.

Currentlygo-snaps has three build in matchers

  • match.Any
  • match.Custom
  • match.Type[ExpectedType]

Open to feedback for building more matchers or you can build your ownexample.

Path Syntax

For JSON go-snaps utilises gjson.

More information about the supported path syntax fromgjson.

As for YAML go-snaps utilisesgithub.com/goccy/go-yaml#5-use-yamlpath.

More information about the supported syntaxPathString.

match.Any

Any matcher acts as a placeholder for any value. It replaces any targeted path with aplaceholder string.

Any("user.name")// or with multiple pathsAny("user.name","user.email")

Any matcher provides some methods for setting options

match.Any("user.name").Placeholder(value).// allows to define a different placeholder value from the default "<Any Value>"ErrOnMissingPath(bool)// determines whether the matcher will err in case of a missing, default true

match.Custom

Custom matcher allows you to bring your own validation and placeholder value

match.Custom("user.age",func(valany) (any,error) {age,ok:=val.(float64)if!ok {returnnil,fmt.Errorf("expected number but got %T",val)}return"some number",nil})

The callback parameter value for JSON can be on of these types:

bool// for JSON booleansfloat64// for JSON numbersstring// for JSON string literalsnil// for JSON nullmap[string]any// for JSON objects[]any// for JSON arrays

If Custom matcher returns an error the snapshot test will fail with that error.

Custom matcher provides a method for setting an option

match.Custom("path",myFunc).Placeholder(value).// allows to define a different placeholder value from the default "<Any Value>"ErrOnMissingPath(bool)// determines whether the matcher will err in case of a missing path, default true

match.Type

Type matcher evaluates types that are passed in a snapshot and it replaces any targeted path with a placeholder in the form of<Type:ExpectedType>.

match.Type[string]("user.info")// or with multiple pathsmatch.Type[float64]("user.age","data.items")

Type matcher provides a method for setting an option

match.Type[string]("user.info").ErrOnMissingPath(bool)// determines whether the matcher will err in case of a missing path, default true

You can see moreexamples.

Configuration

go-snaps allows passing configuration for overriding

  • the directory where snapshots are stored,relative or absolute path
  • the filename where snapshots are stored
  • the snapshot file's extension (regardless the extension the filename will include the.snaps inside the filename)
  • programmatically control whether to update snapshots.You can find an example usage atexamples
  • json config's json format configuration:
    • Width: The maximum width in characters before wrapping json output (default: 80)
    • Indent: The indentation string to use for nested structures (default: 1 spaces)
    • SortKeys: Whether to sort json object keys alphabetically (default: true)
t.Run("snapshot tests",func(t*testing.T) {snaps.WithConfig(snaps.Filename("my_custom_name"),snaps.Dir("my_dir")).MatchSnapshot(t,"Hello Word")s:=snaps.WithConfig(snaps.Dir("my_dir"),snaps.Filename("json_file"),snaps.Ext(".json"),snaps.Update(false),snaps.JSON(snaps.JSONConfig{Width:80,Indent:"    ",SortKeys:false,    }),  )s.MatchJSON(t,`{"hello":"world"}`)})

You can see more onexamples

Update Snapshots

You can update your failing snapshots by settingUPDATE_SNAPS env variable to true.

UPDATE_SNAPS=true gotest ./...

If you don't want to update all failing snapshots, or you want to update only one ofthem you can you use the-run flag to target the test(s) you want.

For more information ongo test flags you can run

gohelptestflag

Clean obsolete snapshots

Summary ObsoleteSummary Removed

go-snaps can identify obsolete snapshots.

In order to enable this functionality you need to useTestMain(m *testing.M) tocallsnaps.Clean(t) after your tests have run. This will also print aSnapshot Summary. (if running testswith verbose flag-v)

If you want to remove the obsolete snap files and snapshots you can runtests withUPDATE_SNAPS=clean env variable.

The reason for usingTestMain is becausego-snaps needs to be sure that all testsare finished so it can keep track of which snapshots were not called.

Example:

funcTestMain(m*testing.M) {v:=m.Run()// After all tests have run `go-snaps` can check for unused snapshotssnaps.Clean(m)os.Exit(v)}

For more information aroundTestMain.

Sort Snapshots

By defaultgo-snaps appends new snaps to the snapshot file and in case of parallel tests the order is random. If you want snaps to be sorted in deterministic order you need to useTestMain per package:

funcTestMain(m*testing.M) {v:=m.Run()// After all tests have run `go-snaps` will sort snapshotssnaps.Clean(m, snaps.CleanOpts{Sort:true})os.Exit(v)}

Skipping Tests

If you want to skip one test usingt.Skip,go-snaps can't keep trackif the test was skipped or if it was removed. For that reasongo-snaps exposesa wrapper fort.Skip,t.Skipf andt.SkipNow, which keep tracks of skipped files.

You can skip, or only run specific tests by using the-run flag.go-snapscan identify which tests are being skipped and parse only the relevant testsfor obsolete snapshots.

Running Tests on CI

Whengo-snaps detects that it is running in CI it will automatically fail when snapshots are missing. This is done to ensure new snapshots are committed alongside the tests and assertions are successful.

go-snaps usesciinfo for detecting if it runs on CI environment.

No Color

go-snaps supports disabling color outputs by running your tests with the env variableNO_COLOR set to any value.

NO_COLOR=true gotest ./...

For more information aroundNO_COLOR.

Snapshots Structure

Snapshots have the form

[TestName - Number]<data>---

TestID is the test name plus an increasing number to allow multiple calls ofMatchSnapshot in a single test.

[TestSimple/should_make_a_map_snapshot - 1]map[string]interface{}{    "mock-0": "value",    "mock-1": int(2),    "mock-2": func() {...},    "mock-3": float32(10.399999618530273),}---

Note

If your snapshot data contain characters--- at the start of a line followed by a new line,go-snaps will "escape" them and save them as/-/-/-/ to differentiate them from termination characters.

Known Limitations

  • When running a specific test file by specifying a pathgo test ./my_test.go,go-snaps can't track the path so it will mistakenly mark snapshots as obsolete.
  • go-snaps doesn't handle CRLF line endings. If you are using Windows, you may need to convert the line endings to LF.
  • go-snaps cannot determine the snapshot path automatically when running withgo test -trimpath ./.... It then instead relies on the current working directory to define the snapshot directory. If this is a problem in your use case you can set an absolute path withsnaps.WithConfig(snaps.Dir("/some/absolute/path"))

Acknowledgments

This library usedJest Snapshoting andCupaloy as inspiration.

  • Jest is a full-fledged Javascript testing framework and has robust snapshoting features.
  • Cupaloy is a great and simple Golang snapshoting solution.
  • Thelogo was made byMariaLetta.

[8]ページ先頭

©2009-2025 Movatter.jp