Contents

snapshot-testing/xc-snapshot-testing

XCSnapshotTesting is a comprehensive, cross-platform snapshot testing library for Swift that enables reliable UI and data structure testing across iOS, macOS, tvOS, watchOS, and visionOS. The library provides powerful tools for capturing, comparing, and verifying snapshots of you

[Documentation](https://swiftpackageindex.com/snapshot-testing/xc-snapshot-testing/main/documentation/xcsnapshottesting)

Check out our comprehensive documentation to get all the necessary information to start using XCSnapshotTesting in your project.

Installation

XCSnapshotTesting can be installed using Swift Package Manager. To include it in your project, add the following dependency to your Package.swift file:

dependencies: [
    .package(url: "https://github.com/snapshot-testing/xc-snapshot-testing", from: "1.0.0")
]

Usage

XCSnapshotTesting provides a comprehensive framework for snapshot testing across different platforms. It supports both synchronous and asynchronous snapshot testing for various types of content including UI elements, data structures, and more.

### Basic Usage

The primary function for snapshot testing is `assert(of:as:named:)`. Here are the most common usage patterns:

#### UI Snapshot Testing

For UI elements like views or view controllers:

```swift
import XCSnapshotTesting
import XCTest

class MyViewControllerTests: XCTestCase {
    func testViewController() async throws {
        let vc = MyViewController()
        
        // Basic snapshot of a view
        try await assert(
            of: vc.view,
            as: .image
        )
        
        // With specific device layout
        try await assert(
            of: vc.view, 
            as: .image(layout: .device(.iPhone15Pro))
        )
        
        // With custom precision and name
        try await assert(
            of: vc.view,
            as: .image(
                precision: 0.95,
                layout: .device(.iPhone15Pro)
            ),
            named: "light-mode"
        )
    }
}
```

#### Data Structure Snapshot Testing

For data structures like JSON, arrays, dictionaries, etc.:

```swift
func testDataStructure() async throws {
    let data = ["name": "John", "age": 30]
    
    // Test as JSON
    try await assert(
        of: data,
        as: .json
    )
    
    // Test as pretty-printed JSON
    try await assert(
        of: data,
        as: .json(pretty: true)
    )
}
```

#### Multiple Snapshots in One Test

You can test multiple configurations in a single test:

```swift
func testMultipleConfigurations() async throws {
    let view = MyCustomView()
    
    // Test with multiple device layouts
    try await assert(
        of: view,
        as: [
            "iPhone": .image(layout: .device(.iPhone15Pro)),
            "iPad": .image(layout: .device(.iPadPro)),
            "Dark": .image(layout: .device(.iPhone15ProDark))
        ]
    )
}
```

### Recording Mode

By default, the library uses the environment's recording mode, but you can override it:

```swift
func testWithRecordingOverride() async throws {
    // Force recording a new snapshot
    try await assert(
        of: myView,
        as: .image,
        record: .always  // or .never to prevent recording
    )
}
```

### Advanced Configuration

You can customize various aspects of snapshot testing:

```swift
func testAdvancedConfiguration() async throws {
    let view = MyCustomView()
    
    try await assert(
        of: view,
        as: .image(
            precision: 0.98,           // Pixel tolerance
            perceptualPrecision: 0.95, // Color/tone tolerance
            layout: .device(.iPhone15Pro),
            delay: 0.1                 // Wait before capturing
        ),
        serialization: .init(          // Additional serialization options
            maxSize: CGSize(width: 1000, height: 1000)
        ),
        named: "custom-snapshot"
    )
}
```

### Testing Environment Configuration

You can configure the testing environment globally:

```swift
override func setUp() {
    super.setUp()
    
    // Configure global testing environment
    withTestingEnvironment(
        record: .never,  // Default to never record
        diffTool: .imageMagick,  // Use ImageMagick for diffs
        maxConcurrentTests: 4
    ) {
        // Tests run with these settings
    }
}
```

### Supported Platforms

XCSnapshotTesting supports:

- iOS (13.0+)
- macOS (10.15+)
- tvOS (13.0+)
- watchOS (6.0+)

### Available Snapshot Types

The library provides various built-in snapshot types:

- `.image` - For UI elements (views, view controllers, etc.)
- `.json` - For JSON-serializable data
- `.text` - For string content
- `.html` - For HTML content
- `.xml` - For XML content
- `.data` - For raw data

### Asynchronous vs Synchronous

The library provides both asynchronous and synchronous versions of the `assert` function:

- Use `assert(of:as:) async` for asynchronous operations
- Use `assert(of:as:)` for synchronous operations

### Custom Dump Snapshots

The package includes an additional module `XCSnapshotTestingCustomDump` that provides snapshot strategies based on the [swift-custom-dump](https://github.com/pointfreeco/swift-custom-dump) library. This allows for human-readable, structured snapshots of complex Swift types:

```swift
import XCSnapshotTesting
import XCSnapshotTestingCustomDump  // Import for custom dump functionality
import XCTest

func testCustomDump() throws {
    struct User {
        let id: Int
        let name: String
        let bio: String
    }
    
    let user = User(id: 1, name: "Blobby", bio: "Blobbed around the world.")
    
    // Test using custom dump representation
    try assert(
        of: user,
        as: .customDump
    )
}
```

### Testing Traits

The library provides a `Traits` system that allows you to customize the environment in which UI elements are rendered during snapshot testing. This is particularly useful for testing different accessibility settings, content size categories, and interface styles:

```swift
func testWithTraits() async throws {
    let view = MyCustomView()
    
    // Test with specific accessibility traits
    var traits = Traits()
    traits.preferredContentSizeCategory = .extraLarge
    traits.userInterfaceStyle = .dark
    
    try await assert(
        of: view,
        as: .image(
            layout: .device(.iPhone15Pro),
            traits: traits
        ),
        named: "accessibility-large-dark"
    )
}
```

This allows you to ensure your UI renders correctly across different accessibility settings, content sizes, and interface styles.

### Device Layouts

The library provides comprehensive device simulation capabilities. You can test your UI across various device sizes, orientations, and split-view configurations:

```swift
func testMultipleDeviceLayouts() async throws {
    let vc = MyViewController()
    
    // Test different iPhone layouts
    try await assert(
        of: vc,
        as: .image(layout: .device(.iPhone15Pro(.portrait)))
    )
    
    try await assert(
        of: vc,
        as: .image(layout: .device(.iPhone15Pro(.landscape)))
    )
    
    // Test iPad layouts including split-view
    try await assert(
        of: vc,
        as: .image(layout: .device(.iPadPro12_9(.landscape(.regular))))
    )
    
    try await assert(
        of: vc,
        as: .image(layout: .device(.iPadPro12_9(.landscape(.medium))))
    )
    
    try await assert(
        of: vc,
        as: .image(layout: .device(.iPadPro12_9(.landscape(.compact))))
    )
}
```

### Alternative Snapshot Methods

In addition to image snapshots, you can also capture recursive descriptions of your views:

```swift
func testRecursiveDescription() async throws {
    let vc = MyViewController()
    
    // Capture the view hierarchy as text
    try await assert(
        of: vc,
        as: .recursiveDescription(layout: .device(.iPhone15Pro))
    )
}
```

### SwiftUI Support

XCSnapshotTesting also supports SwiftUI views directly. You can test SwiftUI views just like UIKit/AppKit views:

```swift
import SwiftUI
import XCSnapshotTesting
import XCTest

@MainActor
func testSwiftUIView() async throws {
    struct MyView: SwiftUI.View {
        var body: some SwiftUI.View {
            HStack {
                Image(systemName: "checkmark.circle.fill")
                Text("Checked").fixedSize()
            }
            .padding(5)
            .background(RoundedRectangle(cornerRadius: 5.0).fill(Color.blue))
            .padding(10)
            .background(Color.yellow)
        }
    }
    
    let view = MyView()
    
    try await assert(
        of: view,
        as: .image  // This will render the SwiftUI view and take a snapshot
    )
    
    // Test with specific sizing
    try await assert(
        of: view,
        as: .image(layout: .sizeThatFits),
        named: "size-that-fits"
    )
}
```

Versioning

We follow semantic versioning for this project. The version number is composed of three parts: MAJOR.MINOR.PATCH.

  • MAJOR version: Increments when there are incompatible changes and breaking changes. These changes may require updates to existing code and could potentially break backward compatibility.
  • MINOR version: Increments when new features or enhancements are added in a backward-compatible manner. It may include improvements, additions, or modifications to existing functionality.
  • The PATCH version includes bug fixes, patches, and safe modifications that address issues, bugs, or vulnerabilities without disrupting existing functionality. It may also include new features, but they must be implemented carefully to avoid breaking changes or compatibility issues.

It is recommended to review the release notes for each version to understand the specific changes and updates made in that particular release.

Contributing

If you find a bug or have an idea for a new feature, please open an issue or submit a pull request. We welcome contributions from the community!

Package Metadata

Repository: snapshot-testing/xc-snapshot-testing

Default branch: main

README: README.md