Skip to content

SnapshotTesting

TestingSwift

What it is

SnapshotTesting records views, data structures and any other value as a reference artefact and fails tests when the output changes.

assertSnapshot records on first run and compares thereafter. Strategies cover images, text descriptions, JSON and recursive dumps.

Installation

.package(url: "https://github.com/pointfreeco/swift-snapshot-testing", from: "1.17.0")

Getting started

The smallest useful thing you can do with it, and what each part means.

View and data snapshots
import SnapshotTesting
import XCTest

final class BookRowTests: XCTestCase {
    func testAppearance() {
        let view = BookRow(book: .preview)

        // Image snapshots across device configurations.
        assertSnapshot(of: view, as: .image(layout: .device(config: .iPhone13)))
        assertSnapshot(of: view, as: .image(traits: .init(userInterfaceStyle: .dark)))

        // Text snapshots are readable in code review, unlike images.
        assertSnapshot(of: viewModel.state, as: .dump)
        assertSnapshot(of: response, as: .json)
    }
}
Text-based strategies such as .dump and .json produce diffs a reviewer can actually read in a pull request, which image snapshots cannot.

Advanced usage

Where the library earns its place over a simpler alternative.

Recording, tolerance and CI
override func setUp() {
    super.setUp()
    // Set to .all to re-record every snapshot after an intentional change.
    // Must be committed as .missing, or tests never fail.
    withSnapshotTesting(record: .missing) {}
}

// Anti-aliasing differs slightly across machines and OS versions.
assertSnapshot(of: view, as: .image(precision: 0.99, perceptualPrecision: 0.98))

// Snapshot a whole request/response pair.
assertSnapshot(of: urlRequest, as: .raw)
Two things bite people: leaving record mode enabled means the suite silently passes forever, and pixel-exact image comparison fails across CI runners — hence the precision tolerance.

Errors and fixes

The failures you are most likely to hit, and what actually resolves them.

Snapshot does not match on CI but passes locally
Different simulator or OS. Pin the runner's Xcode and device, and add perceptualPrecision.
Tests always pass, even after breaking changes
Record mode is still enabled. Set it back to .missing and commit.

Best practices

  • Never commit with record mode on; the tests then always pass.
  • Prefer .dump and .json over image snapshots where possible — the diffs are reviewable.
  • Set a precision tolerance for image snapshots or CI will fail on rendering differences.
  • Pin the simulator and OS version; snapshots are not portable across them.

Background

Why it exists, and what it was reacting to.

From Point-Free, it addresses the tedium of asserting on complex output: rather than writing fifty assertions about a view's layout, record it once and let the diff tell you what changed.