A birdie and insta inspired snapshot testing library for Go.
go get github.com/ptdewey/shutterThe 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@latestpackage package_test
func TestSomething(t *testing.T) {
result := SomeFunction("foo")
shutter.Snap(t, "test title", result)
}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})
}shutter supports data scrubbing and field filtering to handle dynamic or sensitive data in snapshots.
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 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 valuesIgnoreNull()- 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_")
})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().
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...)To review a set of snapshots, run (CLI version -- not recommended):
go run github.com/ptdewey/shutter/cmd/cli reviewShutter 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.goShutter 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)
After installing the TUI:
shuttera- Accept current snapshotr- Reject current snapshots- Skip current snapshotA- Accept all remaining snapshotsR- Reject all remaining snapshotsS- Skip all remaining snapshotsq- Quit
# Accept all new snapshots without review
shutter accept-all
# Reject all new snapshots without review
shutter reject-all