Approval-based snapshot testing library for Go (mirror)
Go 99.8%
Just 0.2%
36 1 0

Clone this repository

https://tangled.org/pdewey.com/shutter https://tangled.org/did:plc:hm5f3dnm6jdhrc55qp2npdja/shutter
git@knot.tangled.wizardry.systems:pdewey.com/shutter git@knot.tangled.wizardry.systems:did:plc:hm5f3dnm6jdhrc55qp2npdja/shutter

For self-hosted knots, clone URLs may differ based on your setup.

Download tar.gz
README.md

Shutter#

A birdie and insta inspired snapshot testing library for Go.

Snapshot review TUI

Installation#

go get github.com/ptdewey/shutter

The review TUI is shipped separately to avoid adding unnecessary project dependencies (installation of the TUI is recommended):

# executable is installed as `shutter`
go install github.com/ptdewey/shutter/cmd/shutter@latest

Usage#

Basic Usage#

package package_test

func TestSomething(t *testing.T) {
    result := SomeFunction("foo")
    shutter.Snap(t, "test title", result)
}

Snapshotting Multiple Values#

Use SnapMany() when you need to snapshot multiple related values together:

func TestMultipleValues(t *testing.T) {
    request := buildRequest()
    response := handleRequest(request)

    // Snapshot both request and response together
    shutter.SnapMany(t, "title", []any{request, response})
}

Advanced Usage: Scrubbers and Ignore Patterns#

shutter supports data scrubbing and field filtering to handle dynamic or sensitive data in snapshots.

Scrubbers#

Scrubbers transform content before snapshotting, typically to replace dynamic or sensitive data with placeholders:

func TestUserAPI(t *testing.T) {
    user := api.GetUser("123")

    // Replace UUIDs and timestamps with placeholders
    shutter.Snap(t, "user", user,
        shutter.ScrubUUID(),
        shutter.ScrubTimestamp(),
    )
}

Built-in Scrubbers:

  • ScrubUUID() - Replaces UUIDs with <UUID>
  • ScrubTimestamp() - Replaces ISO8601 timestamps with <TIMESTAMP>
  • ScrubEmail() - Replaces email addresses with <EMAIL>
  • ScrubIP() - Replaces IPv4 addresses with <IP>
  • ScrubJWT() - Replaces JWT tokens with <JWT>
  • ScrubCreditCard() - Replaces credit card numbers with <CREDIT_CARD>
  • ScrubAPIKey() - Replaces API keys with <API_KEY>
  • ScrubDate() - Replaces various date formats with <DATE>
  • ScrubUnixTimestamp() - Replaces Unix timestamps with <UNIX_TS>

Custom Scrubbers:

// Using regex patterns
shutter.ScrubRegex(`user-\d+`, "<USER_ID>")

// Using exact string matching
shutter.ScrubExact("secret_value", "<REDACTED>")

// Using custom functions
shutter.ScrubWith(func(content string) string {
    return strings.ReplaceAll(content, "localhost", "<HOST>")
})

Ignore Patterns#

Ignore patterns remove specific fields from JSON structures before snapshotting:

func TestAPIResponse(t *testing.T) {
    response := api.GetData()
    jsonBytes, _ := json.Marshal(response)

    // Ignore sensitive fields and null values
    shutter.SnapJSON(t, "response", string(jsonBytes),
        shutter.IgnoreSensitive(),
        shutter.IgnoreNull(),
        shutter.IgnoreKey("created_at", "updated_at"),
    )
}

Built-in Ignore Patterns:

  • IgnoreSensitive() - Ignores common sensitive keys (password, token, api_key, etc.)
  • IgnoreEmpty() - Ignores fields with empty string values
  • IgnoreNull() - Ignores fields with null values

Custom Ignore Patterns:

// Ignore specific keys
shutter.IgnoreKey("id", "timestamp", "version")

// Ignore key-value pairs
shutter.IgnoreKeyValue("status", "pending")

// Ignore keys matching a regex pattern
shutter.IgnoreKeyMatching(`^_.*`) // Ignore all keys starting with underscore

// Ignore specific values
shutter.IgnoreValue("null", "undefined", "")

// Using custom functions
shutter.IgnoreWith(func(key, value string) bool {
    return strings.HasPrefix(key, "temp_")
})

Combining Options#

You can combine multiple scrubbers and ignore patterns:

func TestComplexData(t *testing.T) {
    data := generateTestData()
    jsonBytes, _ := json.Marshal(data)

    shutter.SnapJSON(t, "data", string(jsonBytes),
        // First, remove unwanted fields
        shutter.IgnoreSensitive(),
        shutter.IgnoreKey("debug_info"),
        shutter.IgnoreNull(),

        // Then, scrub dynamic values in remaining fields
        shutter.ScrubUUID(),
        shutter.ScrubTimestamp(),
        shutter.ScrubEmail(),
    )
}

Note: Ignore patterns only work with SnapJSON(). Use scrubbers with Snap(), SnapMany(), or SnapString().

API Reference#

Snapshot Functions:

// For single values (structs, maps, slices, etc.)
shutter.Snap(t, "title", value, options...)

// For multiple related values
shutter.SnapMany(t, "title", []any{value1, value2, value3}, options...)

// For JSON strings (supports both scrubbers and ignore patterns)
shutter.SnapJSON(t, "title", jsonString, options...)

// For plain strings
shutter.SnapString(t, "title", content, options...)

Reviewing Snapshots#

To review a set of snapshots, run (CLI version -- not recommended):

go run github.com/ptdewey/shutter/cmd/cli review

Shutter can also be used programmatically:

// Example: tools/shutter/main.go
package main

import "github.com/ptdewey/shutter"

func main() {
    // This will start the CLI review tool
    shutter.Review()
}

Which can then be run with:

go run tools/shutter/main.go

Shutter also includes (in a separate Go module) a Bubbletea TUI in cmd/tui/main.go. (The TUI is shipped in a separate module to make the added dependencies optional)

TUI Usage#

After installing the TUI:

shutter

Interactive Controls#

  • a - Accept current snapshot
  • r - Reject current snapshot
  • s - Skip current snapshot
  • A - Accept all remaining snapshots
  • R - Reject all remaining snapshots
  • S - Skip all remaining snapshots
  • q - Quit

Alternative Commands#

# Accept all new snapshots without review
shutter accept-all

# Reject all new snapshots without review
shutter reject-all

Other Libraries#