📸 SnapshotTesting
Delightful Swift snapshot testing.
Usage
Once installed, no additional configuration is required. You can import the SnapshotTesting
module and call the assertSnapshot
function.
import SnapshotTesting
import XCTest
class MyViewControllerTests: XCTestCase {
func testMyViewController() {
let vc = MyViewController()
assertSnapshot(matching: vc, as: .image)
}
}
When an assertion first runs, a snapshot is automatically recorded to disk and the test will fail, printing out the file path of any newly-recorded reference.
🛑 failed - No reference was found on disk. Automatically recorded snapshot: …
open "…/MyAppTests/__Snapshots__/MyViewControllerTests/testMyViewController.png"
Re-run "testMyViewController" to test against the newly-recorded snapshot.
Repeat test runs will load this reference and compare it with the runtime value. If they don't match, the test will fail and describe the difference. Failures can be inspected from Xcode's Report Navigator or by inspecting the file URLs of the failure.
You can record a new reference by setting the record
parameter to true
on the assertion or setting isRecording
globally.
assertSnapshot(matching: vc, as: .image, record: true)
// or globally
isRecording = true
assertSnapshot(matching: vc, as: .image)
Snapshot Anything
While most snapshot testing libraries in the Swift community are limited to UIImage
s of UIView
s, SnapshotTesting can work with any format of any value on any Swift platform!
The assertSnapshot
function accepts a value and any snapshot strategy that value supports. This means that a view or view controller can be tested against an image representation and against a textual representation of its properties and subview hierarchy.
assertSnapshot(matching: vc, as: .image)
assertSnapshot(matching: vc, as: .recursiveDescription)
View testing is highly configurable. You can override trait collections (for specific size classes and content size categories) and generate device-agnostic snapshots, all from a single simulator.
assertSnapshot(matching: vc, as: .image(on: .iPhoneSe))
assertSnapshot(matching: vc, as: .recursiveDescription(on: .iPhoneSe))
assertSnapshot(matching: vc, as: .image(on: .iPhoneSe(.landscape)))
assertSnapshot(matching: vc, as: .recursiveDescription(on: .iPhoneSe(.landscape)))
assertSnapshot(matching: vc, as: .image(on: .iPhoneX))
assertSnapshot(matching: vc, as: .recursiveDescription(on: .iPhoneX))
assertSnapshot(matching: vc, as: .image(on: .iPadMini(.portrait)))
assertSnapshot(matching: vc, as: .recursiveDescription(on: .iPadMini(.portrait)))
⚠️ Warning: Snapshots must be compared using a simulator with the same OS, device gamut, and scale as the simulator that originally took the reference to avoid discrepancies between images.
Better yet, SnapshotTesting isn't limited to views and view controllers! There are a number of available snapshot strategies to choose from.
For example, you can snapshot test URL requests (e.g., those that your API client prepares).
assertSnapshot(matching: urlRequest, as: .raw)
// POST http://localhost:8080/account
// Cookie: pf_session={"userId":"1"}
//
// email=blob%40pointfree.co&name=Blob
And you can snapshot test Encodable
values against their JSON and property list representations.
assertSnapshot(matching: user, as: .json)
// {
// "bio" : "Blobbed around the world.",
// "id" : 1,
// "name" : "Blobby"
// }
assertSnapshot(matching: user, as: .plist)
// <?xml version="1.0" encoding="UTF-8"?>
// <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
// <plist version="1.0">
// <dict>
// <key>bio</key>
// <string>Blobbed around the world.</string>
// <key>id</key>
// <integer>1</integer>
// <key>name</key>
// <string>Blobby</string>
// </dict>
// </plist>
In fact, any value can be snapshot-tested by default using its mirror!
assertSnapshot(matching: user, as: .dump)
// ▿ User
// - bio: "Blobbed around the world."
// - id: 1
// - name: "Blobby"
If your data can be represented as an image, text, or data, you can write a snapshot test for it! Check out all of the snapshot strategies that ship with SnapshotTesting and learn how to define your own custom strategies.
Installation
Xcode 11
⚠️ Warning: By default, Xcode will try to add the SnapshotTesting package to your project's main application/framework target. Please ensure that SnapshotTesting is added to a test target instead, as documented in the last step, below.
- From the File menu, navigate through Swift Packages and select Add Package Dependency….
- Enter package repository URL:
https://github.com/pointfreeco/swift-snapshot-testing.git
- Confirm the version and let Xcode resolve the package
- On the final dialog, update SnapshotTesting's Add to Target column to a test target that will contain snapshot tests (if you have more than one test target, you can later add SnapshotTesting to them by manually linking the library in its build phase)
Swift Package Manager
If you want to use SnapshotTesting in any other project that uses SwiftPM, add the package as a dependency in Package.swift
:
dependencies: [
.package(name: "SnapshotTesting", url: "https://github.com/pointfreeco/swift-snapshot-testing.git", from: "1.8.1"),
]
Next, add SnapshotTesting
as a dependency of your test target:
targets: [
.target(name: "MyApp", dependencies: [], path: "Sources"),
.testTarget(name: "MyAppTests", dependencies: ["MyApp", "SnapshotTesting"])
]
Carthage
If you use Carthage, you can add the following dependency to your Cartfile
:
github "pointfreeco/swift-snapshot-testing" ~> 1.8.0
⚠️ Warning: Carthage instructs you to drag frameworks into your Xcode project. Xcode may automatically attempt to link these frameworks to your app target.
SnapshotTesting.framework
is only compatible with test targets, so when you first add it to your project:
- Remove
SnapshotTesting.framework
from any non-test target it may have been added to.- Add
SnapshotTesting.framework
to any applicable test targets.- Add a New Copy Build Phase to any applicable test targets with Destination set to "Frameworks", and add
SnapshotTesting.framework
as an item to this phase.- Do not add
SnapshotTesting.framework
to the "Input Files" or "Output Files" of your app target's Carthagecopy-frameworks
Run Script Phase.See Carthage's "Adding frameworks to unit tests or a framework" documentation for more.
CocoaPods
If your project uses CocoaPods, add the pod to any applicable test targets in your Podfile
:
target 'MyAppTests' do
pod 'SnapshotTesting', '~> 1.8.1'
end
Features
- Dozens of snapshot strategies. Snapshot testing isn't just for
UIView
s andCALayer
s. Write snapshots against any value. - Write your own snapshot strategies. If you can convert it to an image, string, data, or your own diffable format, you can snapshot test it! Build your own snapshot strategies from scratch or transform existing ones.
- No configuration required. Don't fuss with scheme settings and environment variables. Snapshots are automatically saved alongside your tests.
- More hands-off. New snapshots are recorded whether
isRecording
mode istrue
or not. - Subclass-free. Assert from any XCTest case or Quick spec.
- Device-agnostic snapshots. Render views and view controllers for specific devices and trait collections from a single simulator.
- First-class Xcode support. Image differences are captured as XCTest attachments. Text differences are rendered in inline error messages.
- Supports any platform that supports Swift. Write snapshot tests for iOS, Linux, macOS, and tvOS.
- SceneKit, SpriteKit, and WebKit support. Most snapshot testing libraries don't support these view subclasses.
Codable
support. Snapshot encodable data structures into their JSON and property list representations.- Custom diff tool integration.
Plug-ins
-
swift-snapshot-testing-nimble adds Nimble matchers for SnapshotTesting.
-
swift-html is a Swift DSL for type-safe, extensible, and transformable HTML documents and includes an
HtmlSnapshotTesting
module to snapshot test its HTML documents. -
GRDBSnapshotTesting adds snapshot strategy for testing SQLite database migrations made with GRDB.
-
AccessibilitySnapshot adds easy regression testing for iOS accessibility.
Have you written your own SnapshotTesting plug-in? Add it here and submit a pull request!
Related Tools
-
iOSSnapshotTestCase
helped introduce screen shot testing to a broad audience in the iOS community. Experience with it inspired the creation of this library. -
Jest brought generalized snapshot testing to the JavaScript community with a polished user experience. Several features of this library (diffing, automatically capturing new snapshots) were directly influenced.
Learn More
SnapshotTesting was designed with witness-oriented programming.
This concept (and more) are explored thoroughly in a series of episodes on Point-Free, a video series exploring functional programming and Swift hosted by Brandon Williams and Stephen Celis.
Witness-oriented programming and the design of this library was explored in the following Point-Free episodes:
- Episode 33: Protocol Witnesses: Part 1
- Episode 34: Protocol Witnesses: Part 2
- Episode 35: Advanced Protocol Witnesses: Part 1
- Episode 36: Advanced Protocol Witnesses: Part 2
- Episode 37: Protocol-Oriented Library Design: Part 1
- Episode 38: Protocol-Oriented Library Design: Part 2
- Episode 39: Witness-Oriented Library Design
- Episode 40: Async Functional Refactoring
- Episode 41: A Tour of Snapshot Testing 🆓

License
This library is released under the MIT license. See LICENSE for details.
Github
link |
Stars: 1816 |
You may find interesting
Dependencies
Releases
1.8.2 - 2020-08-20T19:45:36
- Rename
SnapshotTesting.record
toSnapshotTesting.isRecording
to avoid clash with XCTestCase's newrecord
method (thanks @xavierLowmiller). - Fix UIApplication selector to return the application as expected (thanks @mstultz).
- Fix key window warning (thanks @tinder-maxwellelliott)
1.8.1 - 2020-06-19T21:03:41
- Change minimum iOS version from iOS 10 to 11 to fix SwiftUI linking problems (thanks @thedavidharris).
1.8.0 - 2020-06-12T13:33:22
- Added: SwiftUI support (thanks @natemann, @regexident).
- Added: Apple TV 4K support (thanks @reez).
- Added: Mac Catalyst support (thanks @rjchatfield).
- Added: UIBezierPath, NSBezierPath, CGPath strategies (thanks @regexident).
- Improved: don't crash on empty images, instead produce error artifact (thanks @mackoj).
- Bug fixed: trait collection now passed to view controller's recursive description strategy (thanks @erikpoort).
- Bug fixed: will no longer crash on hidden/unloaded web views (thanks @stuaustin).
- Bug fixed: don't re-add view controller to window when already added (thanks @hassfers).
- More test coverage (thanks @SatoTakeshiX, @sidepelican).
- Typos fixed (thanks @freak4pc, @jstart).
- Other improvements: timeout error messaging, installation instructions, troubleshooting instructions, Linux CI.
Xcode 11.4 Support - 2020-02-07T22:33:31
This release fixes a bug introduced in the Xcode 11.4 beta with SPM and Swift 5.2.
SwiftPM Tools 5.0 Support - 2019-12-15T15:28:42
This minor release introduces backwards compatibility for SwiftPM Tools @ 5.0.
1.7.0 - 2019-12-11T22:02:49
What's new?
- New snapshot strategy:
wait
(#268)
What's improved?
- Performance improvement: don't diff during recording (#250)
- Documentation improvement: Xcode 11 installation instructions (#256)
- NSView rendering improvements (#260)
- Documentation improvement: SPM test dependency instructions (#265)
- cURL rendering improvements (#273)
Thanks to @mr-v, @f-meloni, @schwmi, @freak4pc, and @ldstreet for contributing to this release!
1.6.0: Bug fixes and performance improvements 😆 - 2019-09-25T16:48:01
What's changed since last time?
- Add device sizes for split view variants of iPads (#209)
- Add recording to inline snapshotting (#212)
- Sort cURL strategy headers (#214)
- Add iOS minimum required deployment target to Package.swift (#215)
- Allow dynamic size of views based on trait collection content sizes (#217)
- Disable bitcode (#221)
- Improve
_assertInlineSnapshot
ergonomics and tests (#231 and #232) - Use
URL.init(fileURLWithPath:isDirectory:)
to avoid file IO (#236) - Speed up image diffing (#248)
- Improve image diff drawing performance (#249)
Thanks to @Sherlouk, @crayment, @jayhickey, @MarianaMeireles, @pavel-y-ivanov, @ferranpujolcamins, @kirillyakimovich, and @mr-v for making this release happen!
Inline snapshots and more strategies - 2019-03-29T20:58:01
What's new
-
@rjchatfield has introduced (#199) a brand new way of snapshotting textual information, called "inline snapshot testing," which automatically inlines snapshots directly in your test file. The 1.5.0 release includes a preview of this functionality! You can use the
_assertInlineSnapshot
helper to try it out. Thanks to @rjchatfield for the PR, and find out more here. -
Two new
URLRequest
snapshot strategies have been included. First, @Sherlouk has included acurl
snapshotting strategy (#203), which snapshots your prepared requests in the cURL format. Also we now have a snapshot strategy for capturing aURLRequest
with its body pretty printed, when possible (#157). This helps makePOST
requests more easily inspectable in the reference file.
Swift 5 Support - 2019-03-26T17:42:39
This release brings SnapshotTesting up to date with Swift 5!
What's changed?
- Swift 5 support!
- The
SnapshotTestCase
subclass is no longer needed on Linux and has been deprecated as a type alias toXCTestCase
. The type alias will be removed in a future version (#200). - The
dump
strategy has been optimized (#195).
11" iPad Pro Support, Better Extensibility, Better Messaging - 2019-03-14T21:55:26
What's new?
- A new view configuration for the 11" iPad Pro's resolution and safe area (#187).
- A few
let
properties onSnapshotting
andDiffing
have been relaxed to bevar
s to make it easier to build strategies from existing ones (#189). - The UIImage diff message has been generalized to read nicely while in record mode (#188).
1.2.0 - 2019-02-20T22:42:52
What's new?
- Small updates to failure messages https://github.com/pointfreeco/swift-snapshot-testing/pull/153
- Renamed generics in
pullback
https://github.com/pointfreeco/swift-snapshot-testing/pull/154 - Support asserting against multiple strategies at once https://github.com/pointfreeco/swift-snapshot-testing/pull/150
- Make XCTAttachment's initializers public #159
- Create a typealias for SnapshotTestCase when not on Linux #169
- Specify custom snapshot directory #170
- Extract image diffing strategies. #172
1.1.0 - 2018-12-18T17:54:04
What's new?
- Updated UIView and UIViewController
recursiveDescription
strategies to support custom trait collections and configuration (#141) - Added a new UIViewController
hierarchy
snapshot strategy (#139) - Added a new
func
strategy on functions that takeCaseIterable
values (#94) - Added a new
description
strategy powered byString.init(describing:)
(#94) - Show diff when recording over previous snapshot (#147)
What's been fixed?
- Improved the
assertSnapshot
function's generic names (#120) - Improved the recording failure message (#135, #140)
- Fixed a crash when snapshotting view controllers with main views configured with auto layout (#142)
- Fixed a crash when running
swift test
against a failing snapshot (#143)
Official release for SnapshotTesting - 2018-12-03T14:03:16
Read more at the official blog post!