snapshot-test-setup
Set up SwiftUI visual regression testing with swift-snapshot-testing. Generates snapshot test boilerplate and CI configuration. Use for UI regression prevention.
Best use case
snapshot-test-setup is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Set up SwiftUI visual regression testing with swift-snapshot-testing. Generates snapshot test boilerplate and CI configuration. Use for UI regression prevention.
Teams using snapshot-test-setup should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/snapshot-test-setup/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How snapshot-test-setup Compares
| Feature / Agent | snapshot-test-setup | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
Set up SwiftUI visual regression testing with swift-snapshot-testing. Generates snapshot test boilerplate and CI configuration. Use for UI regression prevention.
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
Related Guides
SKILL.md Source
# Snapshot Test Setup
Generate SwiftUI snapshot/visual regression tests using Point-Free's swift-snapshot-testing library. Catches unintended UI changes by comparing rendered views against reference images.
## When This Skill Activates
Use this skill when the user:
- Wants "snapshot tests" or "visual regression tests"
- Says "I want to catch UI regressions"
- Asks about "screenshot testing" or "preview testing"
- Wants to verify SwiftUI views don't change unexpectedly
- Mentions "swift-snapshot-testing" or "Point-Free"
## Why Snapshot Tests
```
Without snapshots: With snapshots:
Change a modifier Change a modifier
→ Looks fine locally → Snapshot test fails
→ Push to main → Shows exact visual diff
→ User reports UI bug → Fix before merging
→ Embarrassing → Confidence in UI changes
```
## Pre-Setup Checks
### 1. Project Context
```
Glob: **/Package.swift or **/*.xcodeproj
Grep: "swift-snapshot-testing" (already added?)
Grep: "SnapshotTesting" in test files
```
### 2. Configuration Questions
Ask via AskUserQuestion:
1. **Package manager?**
- Swift Package Manager
- CocoaPods
- Tuist
2. **Platform?**
- iOS
- macOS
- Both
3. **What to test?**
- Specific views (user provides names)
- All screens
- Component library
## Setup Process
### Step 1: Add Dependency
#### Swift Package Manager
```swift
// Package.swift
dependencies: [
.package(
url: "https://github.com/pointfreeco/swift-snapshot-testing",
from: "1.17.0"
)
]
// Test target
.testTarget(
name: "YourAppTests",
dependencies: [
"YourApp",
.product(name: "SnapshotTesting", package: "swift-snapshot-testing")
]
)
```
#### Xcode Project
1. File → Add Package Dependencies
2. URL: `https://github.com/pointfreeco/swift-snapshot-testing`
3. Add `SnapshotTesting` to your test target
### Step 2: Create Snapshot Test Base
```swift
import Testing
import SnapshotTesting
import SwiftUI
@testable import YourApp
// MARK: - Snapshot Configuration
enum SnapshotConfig {
// iOS devices to test
static let iPhoneConfigs: [String: ViewImageConfig] = [
"iPhone_SE": .iPhoneSe,
"iPhone_16": .iPhone13, // Similar dimensions
"iPhone_16_Pro_Max": .iPhone13ProMax
]
// macOS window sizes
static let macOSConfigs: [String: CGSize] = [
"compact": CGSize(width: 400, height: 600),
"regular": CGSize(width: 800, height: 600),
"wide": CGSize(width: 1200, height: 800)
]
// Color schemes to test
static let colorSchemes: [ColorScheme] = [.light, .dark]
}
```
### Step 3: Generate Snapshot Tests
#### iOS View Snapshot
```swift
@Suite("Snapshots: HomeView")
struct HomeViewSnapshotTests {
@Test("matches reference - light mode")
func lightMode() {
let view = HomeView(items: Item.sampleList)
assertSnapshot(
of: UIHostingController(rootView: view),
as: .image(on: .iPhone13)
)
}
@Test("matches reference - dark mode")
func darkMode() {
let view = HomeView(items: Item.sampleList)
.environment(\.colorScheme, .dark)
assertSnapshot(
of: UIHostingController(rootView: view),
as: .image(on: .iPhone13)
)
}
@Test("matches reference - empty state")
func emptyState() {
let view = HomeView(items: [])
assertSnapshot(
of: UIHostingController(rootView: view),
as: .image(on: .iPhone13)
)
}
@Test("matches reference - dynamic type XXL")
func dynamicTypeXXL() {
let view = HomeView(items: Item.sampleList)
.environment(\.sizeCategory, .accessibilityExtraExtraLarge)
assertSnapshot(
of: UIHostingController(rootView: view),
as: .image(on: .iPhone13)
)
}
}
```
#### macOS View Snapshot
```swift
@Suite("Snapshots: SettingsView")
struct SettingsViewSnapshotTests {
@Test("matches reference - standard size")
func standardSize() {
let view = SettingsView()
.frame(width: 500, height: 400)
assertSnapshot(
of: NSHostingController(rootView: view),
as: .image(size: CGSize(width: 500, height: 400))
)
}
@Test("matches reference - dark mode")
func darkMode() {
let view = SettingsView()
.frame(width: 500, height: 400)
.environment(\.colorScheme, .dark)
assertSnapshot(
of: NSHostingController(rootView: view),
as: .image(size: CGSize(width: 500, height: 400))
)
}
}
```
#### Component Snapshot (Reusable)
```swift
@Suite("Snapshots: ItemCard")
struct ItemCardSnapshotTests {
@Test("default state")
func defaultState() {
let view = ItemCard(item: .sample)
.frame(width: 300)
assertSnapshot(of: view, as: .image)
}
@Test("selected state")
func selectedState() {
let view = ItemCard(item: .sample, isSelected: true)
.frame(width: 300)
assertSnapshot(of: view, as: .image)
}
@Test("long title wraps")
func longTitle() {
let item = Item(title: "This is a very long title that should wrap to multiple lines")
let view = ItemCard(item: item)
.frame(width: 300)
assertSnapshot(of: view, as: .image)
}
}
```
### Step 4: Recording Reference Images
First run records reference images (golden masters):
```bash
# Record all snapshots (first run)
xcodebuild test -scheme YourApp \
-destination 'platform=iOS Simulator,name=iPhone 16'
```
**Important:** Reference images are stored in `__Snapshots__/` directories next to test files. Commit these to git.
```
Tests/SnapshotTests/
├── __Snapshots__/
│ └── HomeViewSnapshotTests/
│ ├── lightMode.1.png
│ ├── darkMode.1.png
│ ├── emptyState.1.png
│ └── dynamicTypeXXL.1.png
├── HomeViewSnapshotTests.swift
└── ItemCardSnapshotTests.swift
```
### Step 5: Re-record When Intentional Changes
When you intentionally change a view:
```swift
// Temporarily set record mode
@Test("matches reference - light mode")
func lightMode() {
withSnapshotTesting(record: .all) {
let view = HomeView(items: Item.sampleList)
assertSnapshot(
of: UIHostingController(rootView: view),
as: .image(on: .iPhone13)
)
}
}
```
Or use environment variable:
```bash
SNAPSHOT_TESTING_RECORD=all xcodebuild test -scheme YourApp
```
## What to Snapshot
### High Value (Always Snapshot)
- Screens/pages with multiple states (empty, loaded, error)
- Reusable components in all configurations
- Dark mode vs. light mode
- Dynamic type at standard and accessibility sizes
### Medium Value (Selectively Snapshot)
- Navigation flows (each step)
- Onboarding screens
- Paywall/subscription views
- Settings screens
### Low Value (Skip)
- Views that are 100% system components (plain List, NavigationStack)
- Views that change frequently during active development
- Views dependent on live data
## CI Integration
### GitHub Actions
```yaml
- name: Run Snapshot Tests
run: |
xcodebuild test \
-scheme YourApp \
-destination 'platform=iOS Simulator,name=iPhone 16,OS=18.0' \
-only-testing "YourAppTests/Snapshots" \
-resultBundlePath TestResults.xcresult
- name: Upload Failed Snapshots
if: failure()
uses: actions/upload-artifact@v4
with:
name: failed-snapshots
path: "**/Failures/**"
```
### Xcode Cloud
```bash
# ci_scripts/ci_post_xcodebuild.sh
if [ "$CI_XCODEBUILD_ACTION" = "test" ]; then
# Upload snapshot failures as artifacts
if [ -d "$CI_DERIVED_DATA_PATH" ]; then
find "$CI_DERIVED_DATA_PATH" -name "Failures" -type d \
-exec cp -r {} "$CI_RESULT_BUNDLE_PATH/" \;
fi
fi
```
## Output Format
```markdown
## Snapshot Tests Setup
### Dependency Added
swift-snapshot-testing 1.17.0 via SPM
### Tests Generated
| View | Configurations | Tests |
|------|---------------|-------|
| HomeView | light, dark, empty, XXL type | 4 |
| SettingsView | light, dark | 2 |
| ItemCard | default, selected, long title | 3 |
| **Total** | | **9** |
### Files Created
- `Tests/SnapshotTests/HomeViewSnapshotTests.swift`
- `Tests/SnapshotTests/SettingsViewSnapshotTests.swift`
- `Tests/SnapshotTests/ItemCardSnapshotTests.swift`
### Next Steps
1. Run tests once to record reference images
2. Commit `__Snapshots__/` directories to git
3. Add snapshot test step to CI pipeline
```
## References
- [swift-snapshot-testing](https://github.com/pointfreeco/swift-snapshot-testing)
- `generators/test-generator/` — for unit/integration test generation
- `testing/tdd-feature/` — for TDD workflow with UI featuresRelated Skills
test-data-factory
Generate test fixture factories for your models. Builder pattern and static factories for zero-boilerplate test data. Use when tests need sample data setup.
test-contract
Generate protocol/interface test suites that any implementation must pass. Define the contract once, test every implementation. Use when designing protocols or swapping implementations.
integration-test-scaffold
Generate cross-module test harness with mock servers, in-memory stores, and test configuration. Use when testing networking + persistence + business logic together.
characterization-test-generator
Generates tests that capture current behavior of existing code before refactoring. Use when you need a safety net before AI-assisted refactoring or modifying legacy code.
testing
TDD and testing skills for iOS/macOS apps. Covers characterization tests, TDD workflows, test contracts, snapshot tests, and test infrastructure. Use for test-driven development, adding tests to existing code, or building test infrastructure.
test-spec
Generates comprehensive test specification with unit tests, UI tests, accessibility testing, and beta testing plan. Creates TEST_SPEC.md from PRD and implementation specs. Use when creating QA strategy.
beta-testing
Beta testing strategy for iOS/macOS apps. Covers TestFlight program setup, beta tester recruitment, feedback collection methodology, user interviews, signal-vs-noise interpretation, and go/no-go launch readiness decisions. Use when planning a beta, setting up TestFlight, collecting user feedback, or deciding if ready to launch.
test-generator
Generate test templates for unit tests, integration tests, and UI tests using Swift Testing and XCTest. Use when adding tests to iOS/macOS apps.
persistence-setup
Generates SwiftData or CoreData persistence layer with optional iCloud sync. Use when user wants to add local storage, data persistence, or cloud sync.
offer-codes-setup
Generates offer code distribution strategies and configuration guides for subscription and IAP promotions — including partner campaigns, influencer programs, and email re-engagement. Use when setting up offer codes for distribution.
logging-setup
Generates structured logging infrastructure using os.log/Logger to replace print() statements. Use when user wants to add proper logging, replace print statements, or set up app logging.
localization-setup
Generate internationalization (i18n) infrastructure for multi-language support in iOS/macOS apps. Use when localizing for multiple languages, adopting String Catalogs (xcstrings), or supporting RTL languages.