Snapshot Tests in Rust With Insta and Review Workflow
Insta stores the expected output of a test in a file and shows a diff when it changes. Learn the review workflow with cargo insta, inline snapshots, redactions and when snapshots go wrong.

Writing a 40-line expected JSON document by hand, then updating it each time a field is added, is a chore that tempts people to test less. Snapshot testing hands the chore to a tool: the test records the output once, and later runs compare. What makes it work is the review step. This post shows the workflow with insta, a redaction and the traps. It is the output-heavy case in the testing in Rust guide, and complements assertions.
Rust 1.90, insta 1, cargo-insta installed.
Setup
[dev-dependencies]
insta = { version = "1", features = ["json", "redactions"] }cargo install cargo-instaA first snapshot
pub fn report(name: &str, items: &[(&str, u32)]) -> String {
let mut out = format!("Report for {name}\n");
for (item, qty) in items {
out.push_str(&format!(" {item:<8} x{qty}\n"));
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn renders_a_report() {
insta::assert_snapshot!(report("ana", &[("apple", 3), ("pear", 12)]));
}
}The first cargo test fails and writes src/snapshots/demo__tests__renders_a_report.snap.new. Run cargo insta review, which shows:
-old
+Report for ana
+ apple x3
+ pear x12Press a to accept: the file becomes demo__tests__renders_a_report.snap, to be committed. Rerun and the test passes. Change the format in report and the test fails with a diff of the changed lines.
Structured data
#[derive(serde::Serialize)]
struct Order { id: u64, created_at: String, total: f64 }
#[test]
fn order_json() {
let o = Order { id: 4821, created_at: "2026-11-03T10:00:00Z".into(), total: 19.5 };
insta::assert_json_snapshot!(o, {
".id" => "[id]",
".created_at" => "[timestamp]",
});
}The stored snapshot contains "id": "[id]" and "created_at": "[timestamp]". A redaction selector replaces the value before comparing, so the snapshot does not change when the real id or time does. This is how you snapshot output that includes generated values.
Inline snapshots
insta::assert_snapshot!("a-b", @"a-b");With the @"..." form, the expectation sits in the source, and cargo insta review rewrites it in place. It is nicer for short results. Long inline snapshots clutter the test; switch to files.
CI behavior
In CI, you want a difference to fail and nothing to be written. Set INSTA_UPDATE=no, or use cargo insta test --check, which also fails on unreferenced snapshot files. Never commit .snap.new; add *.snap.new to .gitignore (git ignore for Rust projects).
Where snapshots go wrong
- Accepting without reading. The tool cannot know the new output is correct; you can.
- Huge snapshots. A 3000-line file gets approved on faith. Snapshot the interesting part.
- Non-determinism.
HashMapiteration order, timestamps and random ids make snapshots flaky; redact or sort (see flaky tests). - Testing implementation detail. If the snapshot includes debug formatting of private types, every refactor shows up as a diff.
Limits
Snapshots tell you that output changed, not that it is right. Pair with a few exact assertions on the facts that matter. Crate docs: insta.rs.


