DEV Community

Cover image for Go Snaps: The Latest Updates and Improvements
Georgios Kampitakis
Georgios Kampitakis

Posted on • Edited on

Go Snaps: The Latest Updates and Improvements

More than a year after the previous post about Go Snaps and there have been some improvements and a lot bug fixes.

Introduction

go-snaps is a Go library that makes snapshot testing in Go easier.

Snapshot testing is an important part of the unit testing process. It simplifies writing assertions for complex or dynamic output which can be time consuming and can catch regressions or unintended changes that otherwise might went unnoticed.

Updates to go-snaps

The improvements and updates in the library can be put into 3 categories, visual changes, new features and bug fixes.

Visual Changes

Visual changes are always the one that make the more difference in the experience of using the library. So I focused on making the diff output better more easy to distinguish where the regression is happening in a long output.

An example diff

small diff before
small diff after

or with long diffs

big diff before
big diff after

The second important visual change was on the the reporting of the library and the summary of the tests.

An example when Go Snaps identifies snapshots that are no longer used there is a message prompting cleaning them and showing which tests are marked for deletion.

summary-obsolete

Finally now the library supports NO_COLOR.

New Features

Two new big features landed on Go Snaps on is complimentary to the other.

It's snaps.MatchJSON and property Matchers.

MatchJSON method as the name indicates is for capturing snapshots for JSON structured data. It can accept a valid json in form of string or []byte or whatever value can be passed successfully on json.Marshal.

func TestJSON(t *testing.T) {
  type User struct {
    Age int
    Email string
  }
  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"})
}
Enter fullscreen mode Exit fullscreen mode

JSON will be saved in snapshot in pretty format for better readability.

Having a method that accepts structured data also enabled building another feature that can be useful in snapshot testing, property matchers.

MatchJSON can accept a list of arguments called Matchers. Matchers are functions that can act as property matchers and test values on a specific path of the JSON.

Currently Go Snaps has two builtin matchers

  • match.Any
  • match.Custom

match.Any can be convenient when some fields are dynamic and you have different snapshots on every run, e.g. containing a date or time field.

t.Run("should ignore fields", func(t *testing.T) {
    value := fmt.Sprintf(
        `{"user":"mock-user","age":10,"nested":{"now":["%s"]}}`,
        time.Now(),
    )
    snaps.MatchJSON(t, value, match.Any("nested.now.0"))
})
Enter fullscreen mode Exit fullscreen mode

the resulting snapshot will be

{
 "age": 10,
 "nested": {
  "now": [
   "<Any value>"
  ]
 },
 "user": "mock-user"
}
Enter fullscreen mode Exit fullscreen mode

so you can see we are ignoring the dynamic value there.

On the other hand, match.Custom allows you to build your own matcher that either validates or mutates a specific path.

t.Run("struct marshalling", func(t *testing.T) {
    type User struct {
        Name  string `json:"name"`
        Email string `json:"email"`
        Keys  []int  `json:"keys"`
    }

    u := User{
        Name:  "mock-user",
        Email: "mock-user@email.com",
        Keys:  []int{1, 2, 3, 4, 5},
    }

    snaps.MatchJSON(t, u, match.Custom("keys", func(val interface{}) (interface{}, error) {
        keys, ok := val.([]interface{})
        if !ok {
            return nil, fmt.Errorf("expected []interface{} but got %T", val)
        }

        if len(keys) > 5 {
            return nil, fmt.Errorf("expected less than 5 keys")
        }

        return val, nil
    }))
})
Enter fullscreen mode Exit fullscreen mode

Custom matcher provides a lot of expressiveness and can support very complex use cases. In the future I am thinking of adding support for common used Matchers like type, or Date.

If you are using a Matcher and you think it should added in the library feel free to open an issue or contribute.

For more information around Matchers.

Bug Fixes

As more people start using the library noticed some edge cases ( or not that edge 😅 ) that the library was either not handling gracefully or was misbehaving.

I am not going into much detail but I will share the list of pull requests if you are interested.

  • fix: update isSkippedFile, replace file absolute match w/ regex match by @gparasyris in #48
  • fix: permission denied when running from test suite by @gkampitakis in #44
  • fix: issue with updating snapshots with expand chars, fixes #30
  • fix: issue with marking non child tests as skipped v0.3.0...v0.3.1
  • fix: issue with newlines when removing obsolete snaps v0.3.0...v0.3.1
  • fix: ignore end chars --- inside snapshot in #26

Conclusion

The latest changes and updates to go-snaps improved and simplified using snapshot testing into unit testing workflow. Alongside the bug fixes it makes the library more reliable for people to use it and rely on catching issues and potential regressions.


If you liked or found go-snaps useful you can leave a ⭐️.

Top comments (0)