Snapshot Testing
What it is
Section titled “What it is”A snapshot test renders a value once, saves it as a golden file, and on every later run compares the new output against the stored snapshot. If they differ, the test fails and you either accept the change (updating the snapshot) or fix the bug.
Snapshots are ideal when the output is verbose or structural and writing an explicit assertion by hand would be tedious: serialised objects, rendered HTML, CLI help text.
first run later runs ───────── ────────── render output render output │ │ ▼ ▼ write __snapshots__ compare to stored .snap file │ ├── equal → pass └── differ → fail → review → update?Inline snapshots store the expected value next to the test, so the expectation
is reviewed like normal code instead of hidden in a .snap file.
When to use it / when to avoid it
Section titled “When to use it / when to avoid it”Use it for
- Large structural output: serialised config, ASTs, CLI
--help, error messages - Rendered HTML or component trees where a full-tree diff is useful
- Detecting unintended change in stable, human-reviewed artefacts
- Bootstrapping tests for legacy output you cannot yet assert precisely
Avoid it when
- The value is small — write an explicit
expect(x).toBe(...)instead - The output is inherently unstable (timestamps, random ids, absolute paths)
- The snapshot is accepted blindly on every failure — it then tests nothing
- It becomes a huge opaque blob no reviewer reads
Frameworks & tools
Section titled “Frameworks & tools”| Tool | Ecosystem | Notes |
|---|---|---|
| Vitest snapshots | JS/TS | toMatchSnapshot, toMatchInlineSnapshot, -u to update |
| Jest snapshots | JS/TS | The original; same API as Vitest |
| insta | Rust | assert_snapshot!/assert_debug_snapshot!, cargo insta review |
| Insta / ApprovalTests | .NET | Verify library for approval-style snapshots |
| ApprovalTests | Multi | Golden-master / approval testing across languages |
| Pytest-regressions | Python | data_regression, file_regression fixtures |
| syrupy | Python | Snapshot plugin for pytest |
| Playwright snapshots | JS/TS | toMatchSnapshot for DOM and ARIA trees |
Quick setup
Section titled “Quick setup”npm install -D vitestnpx vitest run # writes missing snapshotsnpx vitest run -u # update changed snapshotspip install syrupy pytestpytest --snapshot-updatego get github.com/gkampitakis/go-snaps# snapshots live in __snapshots__; update with -updatego test ./... -update<dependency> <groupId>com.approvaltests</groupId> <artifactId>approvaltests</artifactId> <version>25.1.0</version> <scope>test</scope></dependency>dotnet add package Verify.XunitExample
Section titled “Example”import { describe, it, expect } from 'vitest';import { renderInvoice } from './invoice';
describe('renderInvoice', () => { it('renders a standard invoice', () => { expect(renderInvoice(sampleOrder)).toMatchSnapshot(); });
it('formats totals inline', () => { expect(formatTotal(1234.5)).toMatchInlineSnapshot('"$1,234.50"'); });});from invoice import render_invoice
def test_renders_invoice(snapshot): assert render_invoice(sample_order) == snapshotimport ( "testing"
"github.com/gkampitakis/go-snaps/snaps")
func TestRenderInvoice(t *testing.T) { snaps.MatchSnapshot(t, RenderInvoice(sampleOrder))}import org.approvaltests.Approvals;import org.junit.jupiter.api.Test;
class InvoiceTest { @Test void rendersInvoice() { Approvals.verify(Invoice.render(sampleOrder)); }}using VerifyXunit;using Xunit;
[UsesVerify]public class InvoiceTests{ [Fact] public Task RendersInvoice() => Verifier.Verify(Invoice.Render(SampleOrder));}Anti-patterns & best practices
Section titled “Anti-patterns & best practices”- Review every snapshot change in the diff. A snapshot is only as good as the
human who reads it; blind
-uturns the test into decoration. - Keep snapshots small and scoped. Snapshot one meaningful artefact, not a whole page with incidental markup.
- Make output deterministic. Freeze time, seed randomness, and normalise paths and ids before snapshotting.
- Prefer inline snapshots for small values. Keeping the expected value in the test makes it reviewable and self-documenting.
- Commit snapshots to version control. They are test data; if they are gitignored they will churn and hide regressions.
- Do not snapshot what an assertion says better.
expect(x).toBe(42)beats a.snapfile containing42.
Self-assessment quiz
Section titled “Self-assessment quiz”Self-assessment quiz
Pick one answer per question, then press Check answers.