Snapshot Testing
Snapshot tests capture formatted output (JSON, HTML, error messages) and compare future runs against committed snapshots. insta provides review workflow and inline snapshots.
Recipe
[dev-dependencies]
insta = { version = "1", features = ["json"] }#[test]
fn renders_user_json() {
let json = serde_json::to_string_pretty(&user()).unwrap();
insta::assert_snapshot!(json);
}When to reach for this: Stable formatted output, CLI help text, serializers, and error display strings.
Working Example
use insta::assert_json_snapshot;
#[test]
fn order_response_shape() {
let resp = OrderResponse {
id: 1,
total_cents: 500,
status: "pending".into(),
};
assert_json_snapshot!(resp);
}cargo test
# on intentional change:
INSTA_UPDATE=1 cargo test order_responseWhat this demonstrates:
- First run creates
snapshots/*.snapfiles - Failures show diff in terminal
INSTA_UPDATE=1accepts new golden outputassert_json_snapshot!redacts unstable fields with settings
Deep Dive
Redactions and Filters
insta::with_settings!({
filters => vec![
(r"\d{4}-\d{2}-\d{2}".to_string(), "[DATE]".to_string()),
],
}, {
assert_snapshot!(format_log_line());
});- Redact timestamps and UUIDs that change every run
- Use
#[insta::snapshot]macro for inline snapshots in source - Review snapshot PRs like golden file updates
Snapshot Layout
tests/snapshots/
└── my_test__renders_user_json.snap
- Commit snapshots to git
- CI fails on drift without update flag
Gotchas
- Snapshots of entire blobs without redaction - flaky on timestamps. Fix: filters or structured field assertions.
- Updating snapshots blindly - hides real bugs. Fix: review diff in PR; pair with unit assertions on critical fields.
- Huge snapshots - hard to review. Fix: snapshot minimal view DTO, not full internal state.
- Cross-platform newline drift - Windows vs Linux CI. Fix: normalize newlines before snapshot.
- Snapshot-only testing of logic - no invariant checks. Fix: assert key properties plus snapshot for format.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
assert_eq! on structs | Small stable values | Large formatted strings |
| Property tests | Algebraic laws | Visual CLI layout |
| Golden binary files | Image/audio output | Text JSON APIs |
FAQs
insta vs assert_eq file?
insta gives diff UI, redaction, inline snapshots, and review workflow.
How inline snapshots work?
insta::assert_snapshot!(val, @"expected"); stores expected in source after INSTA_UPDATE=1.
Can snapshots be optional in CI?
No - they are regression gates. Update intentionally when output changes.
Snapshot async output?
Await in test, snapshot formatted string or JSON body.
Multiple snapshots per test?
Use distinct snapshot names: assert_snapshot!("case_a", out_a).
How test CLI --help?
assert_snapshot!(cmd_help_string()) after clap CommandFactory::command().render_help().
Redact UUIDs?
Regex filter in with_settings! replacing UUID pattern with [UUID].
Snapshot directory per crate?
Default under crate root snapshots/; configure with insta settings in Cargo.toml metadata if needed.
Merge conflicts in snaps?
Re-run INSTA_UPDATE=1 locally after resolving test code conflicts.
Combine with proptest?
Possible but rare; props usually use assertions, not snapshots, due to volume.
Related
- Testing Basics - assertions
- Property-Based Testing - complementary style
- Terminal Output - CLI formatting
- Testing Best Practices - snapshot policy
Stack versions: This page was written for Rust 1.97.0 (edition 2024), Tokio 1.x, Axum 0.8, serde 1.0, sqlx 0.8, clap 4, and Polars 0.46+.