SnapshotTesting

repository·main·Indexed 26 days ago

https://github.com/pointfreeco/swift-snapshot-testing

A Swift library for performing snapshot testing on any value, including views, view controllers, and data structures, across any Swift platform. It supports multiple formats such as .image, .json, .plist, and .recursiveDescription, and integrates with both XCTest and the Swift Testing framework. The library also includes Inline Snapshot Testing for embedding snapshots directly into source code.

Tokens
5.8K
Snippets
18
Records
39
Agent score
87%

What's inside swift-snapshot-testing

  1. Use Snapshotting strategies

    main

    The Snapshotting type defines how to convert a value into a format that can be snapshotted and compared. The library provides a wide variety of built-in strategies for different data types and representations:

    • Visuals: image (with options for precision, perceptualPrecision, size, and drawingMode).
    • Textual/Structural: dump, description, recursiveDescription, elementsDescription, and lines.
    • Data Formats: json, plist, data, curl, and raw.

    Use these strategies within your assertSnapshot calls to specify what aspect of your type you want to test.

  2. Configure snapshot properties for XCTest

    main

    To override snapshot configuration for all tests within an XCTestCase subclass, override the invokeTest() method and wrap the super.invokeTest() call with the withSnapshotTesting(record:diffTool:operation:) function.

    class FeatureTests: XCTestCase {
      override func invokeTest() {
        withSnapshotTesting(record: .failed, diffTool: .ksdiff) {
          super.invokeTest()
        }
      }
    }
  3. Install SnapshotTesting via Xcode

    main

    To add SnapshotTesting to your Xcode project:

    1. From the File menu, navigate to Swift Packages and select Add Package Dependency….
    2. Enter the repository URL: https://github.com/pointfreeco/swift-snapshot-testing.
    3. Confirm the version and let Xcode resolve the package.
    4. Important: In the final dialog, ensure you change the Add to Target column for SnapshotTesting to a test target rather than your main application or framework target.
  4. Use Inline Snapshot Testing

    main

    Inline Snapshot Testing allows you to write string snapshots directly into your test files instead of saving them to separate files on disk. This makes verification easier because the expected value and the assertion sit next to each other.

    To use it, import InlineSnapshotTesting and use the assertInlineSnapshot function. When a test runs for the first time (or when a snapshot is recorded), the library will automatically insert the snapshot as a trailing closure in your test file. You must then re-run the test to verify against the newly-recorded snapshot.

    Warning: When a snapshot is written into a test file, the undo history of that file in Xcode will be lost. Commit your work frequently to version control.

    import InlineSnapshotTesting
    
    assertInlineSnapshot(of: value, as: .json) {
      """
      {
        "id": 42,
        "name": "Blob"
      }
      """
    }
  5. Transform existing strategies using pullback

    main

    You can adapt an existing Snapshotting strategy to work with a different type by using the pullback method. This method takes a transform function that converts the new type into the type expected by the existing strategy.

    For example, if you have a Snapshotting<UIView, UIImage>.image strategy, you can create a strategy for UIViewController by pulling back the UIView strategy using a closure that returns the controller's view.

    extension Snapshotting where Value == UIViewController, Format == UIImage {
      public static let image: Snapshotting = Snapshotting<UIView, UIImage>
        .image
        .pullback { viewController in viewController.view }
    }
  6. Update custom Diffing strategies for Swift Testing support

    main

    In version 1.19, Diffing strategies were updated to support Swift Testing attachments. Previously, Diffing returned XCTAttachment objects, which are incompatible with Swift Testing. You should now use the Diffing.diff(toData:fromData:diffV2:) static method and return DiffAttachment values instead of XCTAttachments.

    To ensure attachments appear in both XCTest and Swift Testing results, use the .data(_:name:) case of the DiffAttachment enum.

    // After migration to 1.19
    
    extension Diffing where Value == MyImage {
      static let myImage = Diffing.diff(
        toData: { $0.pngData()! },
        fromData: { MyImage(data: $0)! }
      ) { old, new in
        guard old != new else { return nil }
        return (
          "Images did not match",
          [
            .data(old.pngData()!, name: "reference.png"),
            .data(new.pngData()!, name: "failure.png"),
          ]
        )
      }
    }
  7. Create asynchronous strategies using asyncPullback

    main

    For types that require asynchronous processing (such as callback-based APIs), use asyncPullback(_:). This method accepts a transform function with the signature (NewStrategyValue) -> Async<ExistingStrategyValue>. This allows you to wrap asynchronous callbacks into the Async type provided by the library.

    Example: Creating an image strategy for WKWebView using its asynchronous takeSnapshot method.

    extension Snapshotting where Value == WKWebView, Format == UIImage {
      public static let image: Snapshotting = Snapshotting<UIImage, UIImage>
        .image
        .asyncPullback { webView in
          Async { callback in
            webView.takeSnapshot(with: nil) { image, error in
              callback(image!)
            }
          }
        }
    }
  8. Configure snapshot properties for Swift Testing

    main

    In Swift Testing, you can override snapshot configuration (like record mode and diffTool) for a single test or an entire suite by applying the .snapshots trait to a @Suite or a @Test function.

    Available configuration keys:

    • record: Changes the mode of assertion to generate and save new snapshots to disk.
    • diffTool: Customizes the command printed in the failure message to open a diff tool (e.g., Kaleidoscope).
    import SnapshotTesting
    
    @Suite(.snapshots(record: .failed, diffTool: .ksdiff))
    struct FeatureTests {
      // All tests in this suite will use these settings
    }
  9. Install SnapshotTesting via Swift Package Manager

    main

    Add SnapshotTesting as a dependency in your Package.swift file. Ensure you add the product to your test target's dependencies, not your main application target.

    dependencies: [
      .package(
        url: "https://github.com/pointfreeco/swift-snapshot-testing",
        from: "1.12.0"
      ),
    ]
    
    targets: [
      .target(name: "MyApp"),
      .testTarget(
        name: "MyAppTests",
        dependencies: [
          "MyApp",
          .product(name: "SnapshotTesting", package: "swift-snapshot-testing"),
        ]
      )
    ]
  10. Use SnapshotTesting with Swift Testing (Beta)

    main

    SnapshotTesting now supports Swift's native Testing library. The assertSnapshot helper automatically detects if it is running in an XCTest or Swift Testing context and uses the appropriate failure mechanism (XCTFail vs Issue.record).

    To configure snapshots in Swift Testing, use the .snapshots test trait on a @Suite or an individual @Test.

    import SnapshotTesting
    
    @Suite(.snapshots(record: .all, diffTool: .ksdiff))
    struct FeatureTests {
      // Tests in this suite will use the specified snapshot configuration
    }