Snapshot Testing Tutorial for SwiftUI: Getting Started

Learn how to test your SwiftUI iOS views in a simple and fast way using snapshot testing. By Vijay Subrahmanian.

Leave a rating/review
Download materials
Save for later
Share

When you’ve meticulously crafted a wonderful UI for your app, making sure it stays intact despite changes in the codebase is important. Among the many paths you can take to achieve this, snapshot testing is an effective option because it’s both lightning-fast and easy to create and maintain.

In this tutorial, you’ll use snapshot testing to validate your UIs, ensuring that code changes haven’t affected the UI you built. You’ll be using a simple library app, Ray’s Library, for creating these tests.

Specifically, you’ll learn how to:

  • Create a baseline snapshot of the UI in your app and use it to validate any changes.
  • Update the baseline snapshot when you’re redesigning the UI.
  • Test the UI for different device types, orientations and traits.

Excited to get started? :]

Getting Started

Use the Download Materials button at the top or bottom of this tutorial to download the starter project.

The starter project is a simple app called Ray’s Library, which shows a list of — you guessed it! — books published by raywenderlich.com. The app was built using SwiftUI and incorporates the SnapshotTesting framework, using Swift Package Manager to test the UI.

Build and run in Xcode. Start by navigating through the app to familiarize yourself with it. The app has two screens, a landing screen and a book detail screen. The landing screen shows a list of books:

Ray's Library landing screen with book list

Scroll through the collection of books. Select any book from the list to view its detail screen, which includes a description and ratings:

Ray's Library details screen

Next, open BookRowView.swift and BookDetailView.swift to learn the UI layout. You’ll write snapshot tests for these UIs in the coming sections. But first, you’ll learn a little more about what snapshot testing is all about.

What Is Snapshot Testing?

Snapshot testing allows you to validate your UIs by comparing a snapshot of the UI at test time with a known valid snapshot of your UI, called a baseline. The test runner compares the current snapshot with the baseline snapshot. If there are any differences between the snapshots, the UI must have changed, so the test fails.

Snapshot Testing Flow

Snapshot Testing Strategies

The SnapshotTesting framework supports testing UIViews and UIViewControllers using the image strategy or the recursive description strategy.

The image strategy takes actual snapshots of your UI as image files, then compares the images pixel by pixel. If the images are different, then the UI changed; your test will fail.

Similarly, the recursive description strategy compares string descriptions of the view hierarchy. If the descriptions have changed, your UI is different. Once again, your test will fail.

Finally, SnapshotTesting offers the hierarchy strategy, which generates the view controller hierarchy as plain text. Changes to the view controller hierarchy will cause your test to fail. Of course, this only works for UIViewController tests.

Using the Image Strategy

For this tutorial, you’ll use the image strategy, where the framework compares the baseline image with the test image. The comparison works by checking for differences in the pixels of both images. Any difference in the pixels will cause your test to fail.

The image strategy supports many options while testing a UIViewController:

  • drawHierarchyInKeyWindow: Bool = false: Renders the view on the simulator’s Key Window and uses its appearance and visual effects.
  • on: ViewImageConfig: Lets you choose a variety of devices as the ViewImageConfig option.
  • precision: Float = 1: Indicates the percentage of pixels that must match for the test to pass. By default, it’s 1, which means 100% of the pixels must match.
  • size: CGSize = nil: The view size for generating the snapshot of the test view.
  • traits: UITraitCollection = .init(): Allows you to specify a plethora of traits to test with.

The first time you write and run a new test case, you’ll find that the test will fail. That’s because you don’t have a baseline snapshot yet. During the first run, SnapshotTesting will save the baseline snapshot to your project directory.

SnapshotTesting saves baseline images to your project directory

Subsequent test runs will take a new snapshot and compare it to the baseline.

Now, you’re ready to create your first snapshot test!

Testing the Row View

You’ll start by adding a test case to validate BookRowView. Open BookRowViewTests.swift, and add the following line after the last import statement:

import SnapshotTesting

This imports the SnapshotTesting framework into your file.

Next, add these lines inside testBookRowView():

let bookRowView = BookRowView(book: sampleBook)
let view: UIView = UIHostingController(rootView: bookRowView).view

Here you’ve created an instance of BookRowView, which is your SwiftUI view.

A SwiftUI view is a like a plan for a view, not a view itself. So you pass it to UIHostingController as its rootView and return a UIView. Now, you have a view object you can test.

Generating a Baseline Snapshot

Before you can test your view, you need to generate a baseline snapshot. Add the line below to the end of testBookRowView():

assertSnapshot(
  matching: view,
  as: .image(size: view.intrinsicContentSize))

Here, you used assertSnapshot(matching:as:) to assert that your view is valid. The SnapshotTesting framework provides assertSnapshot(matching:as:), which is similar to XCTAssert. However, instead of simply comparing two values for equality, assertSnapshot(matching:as:) compares two snapshots.

In this case, you passed your view as the matching parameter to tell SnapshotTesting which view you want to test. You’ve also directed SnapshotTesting to use the .image strategy, meaning it will actually save a graphical representation of your UI view at its intrinsic content size.

Note: The image generated with an intrinsic content size might be larger than the iPhone’s width and/or height, depending on its contents. Therefore, it’s best to specify the appropriate size explicitly before testing a view.

Build and run the test by selecting Product ▸ Test from the menu bar or by typing Command-U. This will run all the tests in the test target.

Test failed message

Your test failed. To read the failure message, expand it by clicking the Cross icon.

SnapshotTesting reports no reference snapshot in error message

The test failed because there was no reference snapshot to compare to the test result. Remember, the first time you execute your test case, the SnapshotTesting framework will create the baseline for you.

Now that you’ve created the baseline snapshot, build and run again. Success!

SnapshotTesting tests pass after you create the baseline

Great! Your first snapshot test