Approval-based snapshot testing library for Go (mirror)
at main 245 lines 6.1 kB view raw view rendered
1# Shutter 2 3A [birdie](https://github.com/giacomocavalieri/birdie) and [insta](https://github.com/mitsuhiko/insta) inspired snapshot testing library for Go. 4 5![Snapshot review TUI](./assets/screenshot-tui.png "Snapshot diff view (TUI)") 6 7## Installation 8 9```sh 10go get github.com/ptdewey/shutter 11``` 12 13The review TUI is shipped separately to avoid adding unnecessary project dependencies (installation of the TUI is recommended): 14 15```sh 16# executable is installed as `shutter` 17go install github.com/ptdewey/shutter/cmd/shutter@latest 18``` 19 20## Usage 21 22### Basic Usage 23 24```go 25package package_test 26 27func TestSomething(t *testing.T) { 28 result := SomeFunction("foo") 29 shutter.Snap(t, "test title", result) 30} 31``` 32 33### Snapshotting Multiple Values 34 35Use `SnapMany()` when you need to snapshot multiple related values together: 36 37```go 38func TestMultipleValues(t *testing.T) { 39 request := buildRequest() 40 response := handleRequest(request) 41 42 // Snapshot both request and response together 43 shutter.SnapMany(t, "title", []any{request, response}) 44} 45``` 46 47### Advanced Usage: Scrubbers and Ignore Patterns 48 49shutter supports data scrubbing and field filtering to handle dynamic or sensitive data in snapshots. 50 51#### Scrubbers 52 53Scrubbers transform content before snapshotting, typically to replace dynamic or sensitive data with placeholders: 54 55```go 56func TestUserAPI(t *testing.T) { 57 user := api.GetUser("123") 58 59 // Replace UUIDs and timestamps with placeholders 60 shutter.Snap(t, "user", user, 61 shutter.ScrubUUID(), 62 shutter.ScrubTimestamp(), 63 ) 64} 65``` 66 67**Built-in Scrubbers:** 68 69- `ScrubUUID()` - Replaces UUIDs with `<UUID>` 70- `ScrubTimestamp()` - Replaces ISO8601 timestamps with `<TIMESTAMP>` 71- `ScrubEmail()` - Replaces email addresses with `<EMAIL>` 72- `ScrubIP()` - Replaces IPv4 addresses with `<IP>` 73- `ScrubJWT()` - Replaces JWT tokens with `<JWT>` 74- `ScrubCreditCard()` - Replaces credit card numbers with `<CREDIT_CARD>` 75- `ScrubAPIKey()` - Replaces API keys with `<API_KEY>` 76- `ScrubDate()` - Replaces various date formats with `<DATE>` 77- `ScrubUnixTimestamp()` - Replaces Unix timestamps with `<UNIX_TS>` 78 79**Custom Scrubbers:** 80 81```go 82// Using regex patterns 83shutter.ScrubRegex(`user-\d+`, "<USER_ID>") 84 85// Using exact string matching 86shutter.ScrubExact("secret_value", "<REDACTED>") 87 88// Using custom functions 89shutter.ScrubWith(func(content string) string { 90 return strings.ReplaceAll(content, "localhost", "<HOST>") 91}) 92``` 93 94#### Ignore Patterns 95 96Ignore patterns remove specific fields from JSON structures before snapshotting: 97 98```go 99func TestAPIResponse(t *testing.T) { 100 response := api.GetData() 101 jsonBytes, _ := json.Marshal(response) 102 103 // Ignore sensitive fields and null values 104 shutter.SnapJSON(t, "response", string(jsonBytes), 105 shutter.IgnoreSensitive(), 106 shutter.IgnoreNull(), 107 shutter.IgnoreKey("created_at", "updated_at"), 108 ) 109} 110``` 111 112**Built-in Ignore Patterns:** 113 114- `IgnoreSensitive()` - Ignores common sensitive keys (password, token, api_key, etc.) 115- `IgnoreEmpty()` - Ignores fields with empty string values 116- `IgnoreNull()` - Ignores fields with null values 117 118**Custom Ignore Patterns:** 119 120```go 121// Ignore specific keys 122shutter.IgnoreKey("id", "timestamp", "version") 123 124// Ignore key-value pairs 125shutter.IgnoreKeyValue("status", "pending") 126 127// Ignore keys matching a regex pattern 128shutter.IgnoreKeyMatching(`^_.*`) // Ignore all keys starting with underscore 129 130// Ignore specific values 131shutter.IgnoreValue("null", "undefined", "") 132 133// Using custom functions 134shutter.IgnoreWith(func(key, value string) bool { 135 return strings.HasPrefix(key, "temp_") 136}) 137``` 138 139#### Combining Options 140 141You can combine multiple scrubbers and ignore patterns: 142 143```go 144func TestComplexData(t *testing.T) { 145 data := generateTestData() 146 jsonBytes, _ := json.Marshal(data) 147 148 shutter.SnapJSON(t, "data", string(jsonBytes), 149 // First, remove unwanted fields 150 shutter.IgnoreSensitive(), 151 shutter.IgnoreKey("debug_info"), 152 shutter.IgnoreNull(), 153 154 // Then, scrub dynamic values in remaining fields 155 shutter.ScrubUUID(), 156 shutter.ScrubTimestamp(), 157 shutter.ScrubEmail(), 158 ) 159} 160``` 161 162**Note:** Ignore patterns only work with `SnapJSON()`. Use scrubbers with `Snap()`, `SnapMany()`, or `SnapString()`. 163 164#### API Reference 165 166**Snapshot Functions:** 167 168```go 169// For single values (structs, maps, slices, etc.) 170shutter.Snap(t, "title", value, options...) 171 172// For multiple related values 173shutter.SnapMany(t, "title", []any{value1, value2, value3}, options...) 174 175// For JSON strings (supports both scrubbers and ignore patterns) 176shutter.SnapJSON(t, "title", jsonString, options...) 177 178// For plain strings 179shutter.SnapString(t, "title", content, options...) 180``` 181 182### Reviewing Snapshots 183 184To review a set of snapshots, run (CLI version -- not recommended): 185 186```sh 187go run github.com/ptdewey/shutter/cmd/cli review 188``` 189 190Shutter can also be used programmatically: 191 192```go 193// Example: tools/shutter/main.go 194package main 195 196import "github.com/ptdewey/shutter" 197 198func main() { 199 // This will start the CLI review tool 200 shutter.Review() 201} 202``` 203 204Which can then be run with: 205 206```sh 207go run tools/shutter/main.go 208``` 209 210Shutter also includes (in a separate Go module) a [Bubbletea](https://github.com/charmbracelet/bubbletea) TUI in [cmd/tui/main.go](./cmd/tui/main.go). 211(The TUI is shipped in a separate module to make the added dependencies optional) 212 213### TUI Usage 214 215After installing the TUI: 216 217```sh 218shutter 219``` 220 221#### Interactive Controls 222 223- `a` - Accept current snapshot 224- `r` - Reject current snapshot 225- `s` - Skip current snapshot 226- `A` - Accept all remaining snapshots 227- `R` - Reject all remaining snapshots 228- `S` - Skip all remaining snapshots 229- `q` - Quit 230 231#### Alternative Commands 232 233```sh 234# Accept all new snapshots without review 235shutter accept-all 236 237# Reject all new snapshots without review 238shutter reject-all 239``` 240 241## Other Libraries 242 243- [go-snaps](https://github.com/gkampitakis/go-snaps) 244 - shutter uses the diff implementation from `go-snaps`. 245- [cupaloy](https://github.com/bradleyjkemp/cupaloy)