Help us shape the future of Go! Take the 2026 Go Developer Survey

shutter

package module
v0.2.4 Latest Latest
Warning

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

Go to latest
Published: May 10, 2026 License: MIT Imports: 8 Imported by: 0

README

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

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func AcceptAll

func AcceptAll() error

AcceptAll accepts all pending snapshot changes without review.

func RejectAll

func RejectAll() error

RejectAll rejects all pending snapshot changes without review.

func Review

func Review() error

Review launches an interactive review session to accept or reject snapshot changes.

func Snap

func Snap(t snapshots.T, title string, value any, opts ...Option)

Snap takes a single value, formats it, and creates a snapshot with the given title. Complex types are formatted using a pretty-printer for readability.

Options can be provided to scrub sensitive or dynamic data before snapshotting. Only Scrubber options are supported; IgnorePattern options will cause an error.

Example:

user := User{ID: "123", Email: "user@example.com"}
shutter.Snap(t, "user data", user,
    shutter.ScrubUUID(),
    shutter.ScrubEmail(),
)

func SnapJSON

func SnapJSON(t snapshots.T, title string, jsonStr string, opts ...Option)

SnapJSON takes a JSON string, validates it, and pretty-prints it with consistent formatting before snapshotting. This preserves the raw JSON format while ensuring valid JSON structure.

Options can be provided to apply both Scrubbers and IgnorePatterns. IgnorePatterns remove fields from the JSON structure before scrubbing. Scrubbers then transform the remaining content.

Example:

jsonStr := `{"id": "550e8400-...", "email": "user@example.com", "password": "secret"}`
shutter.SnapJSON(t, "user response", jsonStr,
    shutter.IgnoreKey("password"),     // First: remove password field
    shutter.ScrubUUID(),               // Second: scrub remaining UUIDs
    shutter.ScrubEmail(),              // Third: scrub emails
)

func SnapMany

func SnapMany(t snapshots.T, title string, values []any, opts ...Option)

SnapMany takes multiple values, formats them, and creates a snapshot with the given title. This is useful when you want to snapshot multiple related values together.

Options can be provided to scrub sensitive or dynamic data before snapshotting. Only Scrubber options are supported; IgnorePattern options will cause an error.

Example:

shutter.SnapMany(t, "request and response",
    []any{request, response},
    shutter.ScrubUUID(),
    shutter.ScrubTimestamp(),
)

func SnapString

func SnapString(t snapshots.T, title string, content string, opts ...Option)

SnapString takes a string value and creates a snapshot with the given title. This is useful for snapshotting generated text, logs, or other string content.

Options can be provided to scrub sensitive or dynamic data before snapshotting. Only Scrubber options are supported; IgnorePattern options will cause an error.

Example:

output := generateReport()
shutter.SnapString(t, "report output", output,
    shutter.ScrubTimestamp(),
)

Types

type IgnorePattern

type IgnorePattern interface {
	Option
	ShouldIgnore(key, value string) bool
}

IgnorePattern determines whether a key-value pair should be excluded from JSON snapshots. This is useful for removing fields that change frequently or contain sensitive data.

IgnorePatterns only work with SnapJSON. Using them with Snap or SnapString will result in an error.

func IgnoreEmpty

func IgnoreEmpty() IgnorePattern

IgnoreEmpty ignores fields with empty string values.

This option only works with SnapJSON.

Example:

shutter.SnapJSON(t, "response", jsonStr,
    shutter.IgnoreEmpty(),
)

func IgnoreKey

func IgnoreKey(keys ...string) IgnorePattern

IgnoreKey creates an ignore pattern that ignores the specified keys regardless of their values.

This option only works with SnapJSON.

Example:

shutter.SnapJSON(t, "response", jsonStr,
    shutter.IgnoreKey("password", "secret", "token"),
)

func IgnoreKeyMatching

func IgnoreKeyMatching(pattern string) IgnorePattern

IgnoreKeyMatching creates an ignore pattern that ignores keys matching the given regex pattern.

This option only works with SnapJSON.

Example:

shutter.SnapJSON(t, "response", jsonStr,
    shutter.IgnoreKeyMatching(`^user_`),
)

func IgnoreKeyPattern

func IgnoreKeyPattern(keyPattern, valuePattern string) IgnorePattern

IgnoreKeyPattern creates an ignore pattern using regex patterns for keys and values. Pass empty string for keyPattern or valuePattern to match any key or value.

This option only works with SnapJSON.

Example:

shutter.SnapJSON(t, "response", jsonStr,
    shutter.IgnoreKeyPattern(`.*password.*`, ""),
    shutter.IgnoreKeyPattern(`.*token.*`, ""),
)

func IgnoreKeyValue

func IgnoreKeyValue(key, value string) IgnorePattern

IgnoreKeyValue creates an ignore pattern that matches exact key-value pairs. Use "*" as the value to ignore any value for the given key.

This option only works with SnapJSON.

Example:

shutter.SnapJSON(t, "response", jsonStr,
    shutter.IgnoreKeyValue("password", "*"),
    shutter.IgnoreKeyValue("status", "pending"),
)

func IgnoreNull

func IgnoreNull() IgnorePattern

IgnoreNull ignores fields with null/nil values (represented as "null" in JSON).

This option only works with SnapJSON.

Example:

shutter.SnapJSON(t, "response", jsonStr,
    shutter.IgnoreNull(),
)

func IgnoreSensitive

func IgnoreSensitive() IgnorePattern

IgnoreSensitive ignores common sensitive key names like password, token, etc.

This option only works with SnapJSON.

Example:

shutter.SnapJSON(t, "response", jsonStr,
    shutter.IgnoreSensitive(),
)

func IgnoreValue

func IgnoreValue(values ...string) IgnorePattern

IgnoreValue creates an ignore pattern that ignores the specified values regardless of their keys.

This option only works with SnapJSON.

Example:

shutter.SnapJSON(t, "response", jsonStr,
    shutter.IgnoreValue("pending", "processing"),
)

func IgnoreWith

func IgnoreWith(ignoreFunc func(key, value string) bool) IgnorePattern

IgnoreWith creates an ignore pattern using a custom function. The function receives the key and value and should return true if the key-value pair should be ignored.

This option only works with SnapJSON.

Example:

shutter.SnapJSON(t, "response", jsonStr,
    shutter.IgnoreWith(func(key, value string) bool {
        return strings.HasPrefix(key, "temp_")
    }),
)

type Option

type Option interface {
	// contains filtered or unexported methods
}

Option is a marker interface for all snapshot options. This allows compile-time type safety while supporting different option types.

type Scrubber

type Scrubber interface {
	Option
	Scrub(content string) string
}

Scrubber transforms content before snapshotting, typically to replace dynamic or sensitive data with stable placeholders.

Scrubbers are applied in the order they are provided. Later scrubbers can transform the output of earlier scrubbers.

Example:

shutter.Snap(t, "user data", user,
    shutter.ScrubUUID(),        // First: UUIDs -> <UUID>
    shutter.ScrubEmail(),       // Second: emails -> <EMAIL>
)

func ScrubAPIKey

func ScrubAPIKey() Scrubber

ScrubAPIKey replaces common API key patterns with "<API_KEY>". Matches patterns like: sk_live_..., pk_test_..., api_key_...

Example:

shutter.Snap(t, "config", config, shutter.ScrubAPIKey())

func ScrubCreditCard

func ScrubCreditCard() Scrubber

ScrubCreditCard replaces credit card numbers with "<CREDIT_CARD>".

Example:

shutter.Snap(t, "payment", payment, shutter.ScrubCreditCard())

func ScrubDate

func ScrubDate() Scrubber

ScrubDate replaces various date formats with "<DATE>".

Example:

shutter.Snap(t, "data", data, shutter.ScrubDate())

func ScrubEmail

func ScrubEmail() Scrubber

ScrubEmail replaces email addresses with "<EMAIL>".

Example:

shutter.Snap(t, "user", user, shutter.ScrubEmail())

func ScrubExact

func ScrubExact(match string, replacement string) Scrubber

ScrubExact creates a scrubber that replaces exact string matches.

Example:

shutter.ScrubExact("secret_value", "<REDACTED>")

func ScrubIP

func ScrubIP() Scrubber

ScrubIP replaces IPv4 addresses with "<IP>".

Example:

shutter.Snap(t, "request", request, shutter.ScrubIP())

func ScrubJWT

func ScrubJWT() Scrubber

ScrubJWT replaces JWT tokens with "<JWT>".

Example:

shutter.Snap(t, "auth", authData, shutter.ScrubJWT())

func ScrubRegex

func ScrubRegex(pattern string, replacement string) Scrubber

ScrubRegex creates a scrubber that replaces all matches of the given regex pattern with the replacement string.

Example:

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

func ScrubTimestamp

func ScrubTimestamp() Scrubber

ScrubTimestamp replaces ISO8601 timestamps with "<TIMESTAMP>".

Example:

shutter.Snap(t, "event", event, shutter.ScrubTimestamp())

func ScrubUUID

func ScrubUUID() Scrubber

ScrubUUID replaces all UUIDs with "<UUID>".

Example:

shutter.Snap(t, "user", user, shutter.ScrubUUID())

func ScrubUnixTimestamp

func ScrubUnixTimestamp() Scrubber

ScrubUnixTimestamp replaces Unix timestamps (10-13 digits) with "<UNIX_TS>". Note: This is aggressive and may match other long numbers. For more conservative scrubbing with context keywords, use ScrubRegex with a custom pattern.

Example:

shutter.Snap(t, "data", data, shutter.ScrubUnixTimestamp())

func ScrubWith

func ScrubWith(scrubFunc func(string) string) Scrubber

ScrubWith creates a scrubber using a custom function. The function receives the snapshot content and should return the scrubbed content.

Example:

shutter.Snap(t, "data", data,
    shutter.ScrubWith(func(content string) string {
        return strings.ReplaceAll(content, "localhost", "<HOST>")
    }),
)

Directories

Path Synopsis
cmd
cli command
shutter module
tui module
internal

Jump to

Keyboard shortcuts

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