Snapshot Testing
Snapshot testing compares actual output against a stored reference file. This is useful for verifying that scan results, generated files, or command output haven't changed unexpectedly.
When to Use Snapshots
| Scenario | Approach |
|---|---|
| Simple exit code check | exitCode: 0 |
| Output contains a substring | stdout.contains |
| Exact output match | Snapshot assertion |
| Complex multi-line output | Snapshot assertion |
| Generated file verification | File + snapshot assertion |
Use snapshots when the output is complex enough that inline assertions become unwieldy.
Defining Snapshot Assertions
Test definitions can include snapshot assertions. Here's a real example:
"functional-examples": {
"title": "Greeting Function",
"test": [
{
"name": "scan output matches snapshot",
"steps": [
{
"command": "bash ../../scan.sh",
"assertions": {
"exitCode": 0
}
},
{
"command": "true",
"assertions": {
"snapshot": {
"path": "../../output.txt",
"snapshot": "../../__snapshots__/scan-output.txt"
}
}
}
]
},
{
"name": "cleanup",
"options": {
"command": "rm -f ../../output.txt"
},
"assertions": {
"exitCode": 0
}
}
]
},
Or check multiple snapshots:
assertions:
snapshots:
- path: dist/index.js
snapshot: __snapshots__/index.js
- path: dist/types.d.ts
snapshot: __snapshots__/types.d.ts
First-Run Behavior
On the first run, if the snapshot file doesn't exist, the test runner creates it from the actual output. Subsequent runs compare against the stored snapshot.
Updating Snapshots
When output intentionally changes, update snapshots with the -u flag:
npx functional-examples test -u
This overwrites stored snapshot files with current output.
Multi-Step Snapshot Workflow
"functional-examples": {
"title": "Greeting Function",
"test": [
{
"name": "scan output matches snapshot",
"steps": [
{
"command": "bash ../../scan.sh",
"assertions": {
"exitCode": 0
}
},
{
"command": "true",
"assertions": {
"snapshot": {
"path": "../../output.txt",
"snapshot": "../../__snapshots__/scan-output.txt"
}
}
}
]
},
{
"name": "cleanup",
"options": {
"command": "rm -f ../../output.txt"
},
"assertions": {
"exitCode": 0
}
}
]
},
This pattern:
- Runs the scan and captures output to a file
- Compares the file against a stored snapshot
- Cleans up temporary files
The scan script strips non-deterministic output (like timing) before saving:
#!/usr/bin/env bash
# Scan for examples and save output to a file for snapshot comparison
cd "$(dirname "$0")"
# Run the scan and capture output, stripping the timing line (non-deterministic)
npx functional-examples scan | sed '/^Scan completed in/d' > output.txt
And here's the stored snapshot it compares against:
Found 2 example(s)
ID Title Files Extractor
------------------------------------------------------------------------------------------
snapshot-greeting Greeting Function 1 javascript-extractor
greeting-example Greeting Function 2 javascript-extractor
Snapshots for Generated Content
Snapshots are especially powerful for verifying generated files. The documentation plugin example uses a snapshot to verify the markdown output produced by functional-examples documentation:
---
generated: true
---
# Sample for Documentation
A simple example demonstrating documentation generation
## `src/sample.ts`
### Region: `setup`
```typescript
import { readFileSync } from 'node:fs';
/**
* Read and parse a configuration file.
*/
export function loadConfig(path: string): Record<string, unknown> {
const content = readFileSync(path, 'utf-8');
return JSON.parse(content);
}
Region: usage
// Load configuration from a JSON file
const config = loadConfig('config.json');
console.log('Loaded config:', config);
This ensures that changes to the documentation template or plugin logic are caught immediately by the test suite.
## Annotating Snapshots for Documentation
Snapshot files can include region tags so documentation can extract specific sections:
```text
#_region scan-result
Found 1 example(s)
...
#_endregion scan-result
Documentation can then reference: <%= example('snapshot-testing').region('scan-result') %>
Parser Pipeline Transparency
Region tags in snapshot files are processed by the parser pipeline — they're stripped before comparison. This means you can annotate snapshots with #_region tags for documentation without affecting test assertions.
Anti-Patterns
- Don't use
--update-snapshotsin CI — CI should detect drift, not silently fix it - Don't snapshot non-deterministic output — timestamps, random IDs, and absolute paths make snapshots flaky
- Don't use snapshots for simple checks —
stdout.containsis more resilient to formatting changes
See also: Test Plugin for the full assertion reference, CI Integration for running snapshot tests in pipelines.