Swift Snapshot Testing

Screenshotbot integrates with the popular swift-snapshot-testing library. In this document we’ll walk you through the steps required to set up the integration.

You have two options:

  • Use our fork of swift-snapshot-testing, which has default behaviors that handle both images and text snapshots seamlessly.
  • Or use the upstream version, but create wrapper functions around assertSnapshot to always pass record: true, and use this wrapper function when taking snapshots of views/images.

Using our fork

Update your swift-snapshot-testing to point to https://github.com/tdrhq/swift-snapshot-testing. We try to keep this synced with the latest released version from upstream.

Once you use this fork, the test behavior changes:

  • For non-image snapshots, the behavior is unchanged. This means that your text-based snapshots can co-exist with Screenshotbot.
  • For snapshots that produce a png file, we will record the new image, but the tests will never fail: i.e. it won't fail if the snapshot has changed, and it won't fail if the image is not present.
  • Additionally, all images are always added to the test results bundle (required for Xcode Cloud support)

Instead of having tests fail, we'll upload the png files to Screenshotbot in the next step (we will not upload during the test run). If you need to override this temporarily while developing a feature, you can set screenshotbotMode = false.

Next you need to add all the image snapshots to your .gitignore:

**/__Snapshots__/**/*.png

Finally in your CI, you need to look find all __Snapshots__ directories, and upload them to Screenshotbot. In Fastlane, this might look like:

lane :fetch_screenshotbot do
   sh "curl https://cdn.screenshotbot.io/recorder.sh | sh"
end

lane :screenshotbot_upload_all do
  # For each __Snapshots__ directory, load it to screenshotbot
  Dir.glob("../**/__Snapshots__/").each do |dir|
    channel_name = dir[3..-("/__Snapshots__/".length + 1)]
    sh "~/screenshotbot/recorder --channel #{channel_name} --directory #{dir} --recursive"
  end
end

lane :screenshotbot_ci do
  tests
  fetch_screenshotbot
  screenshotbot_upload_all
end

You can now call bundle exec fastlane screenshotbot_ci instead of bundle exec fastlane tests from your CI.

For a full example of an integration using Fastlane and CircleCI, see this repository.

Using the upstream version of swift-snapshot-testing

To use the upstream version, typically our customers would create a new assertScreenshot function that calls into assertSnapshot with record: true.

Ideally, you also need to be override this for developers trying to verify screenshots locally, although developers could use our Local Runs feature instead.

Once you have this you would still update your .gitignore:

**/__Snapshots__/**/*.png

And finally you would update your CI to call the Screenshotbot CLI tool to upload all the screenshots. See the Fastlane script in the previous section for an example.

We'll add some sample code for this integration in the future. In the meantime, please reach out if you need help.

Xcode Cloud

Screenshotbot integrates with Xcode Cloud, but requires a different configuration approach compared to traditional CI systems. Xcode Cloud runs each step in a limited environment, so we need to split the integration across multiple CI scripts.

Prerequisites

Make sure you use the latest version of our fork. As mentioned before this ensures the result images are part of the .xcresult from the test.

Post-clone setup (ci_scripts/ci_post_clone.sh)

Create or update your ci_scripts/ci_post_clone.sh file with:

#!/bin/sh

curl https://screenshotbot.io/recorder.sh | sh
~/screenshotbot/recorder ci upload-commit-graph --main-branch main

This script downloads the Screenshotbot recorder and uploads the commit graph information. Screenshotbot needs to have git ancestor information, and the Git repository will not be available in the post-build step. The --main-branch argument is also required, although we might remove this requirement in the future.

Post-build upload (ci_scripts/ci_post_xcodebuild.sh)

Create or update your ci_scripts/ci_post_xcodebuild.sh file with:

#!/bin/sh

if [[ "$CI_XCODEBUILD_ACTION" == "test-without-building" ]] ; then
    curl https://screenshotbot.io/recorder.sh | sh
    ~/screenshotbot/recorder --xcresult $CI_RESULT_BUNDLE_PATH \
       --channel channel-name \
       --commit-limit 0
fi

Replace channel-name with your project name.

This script runs after your tests complete and uploads the test results (including screenshots) to Screenshotbot. Make sure to replace swift-snapshot-testing-example-xcode-cloud with your desired channel name.

Make scripts executable

Ensure both scripts are executable:

chmod +x ci_scripts/ci_post_clone.sh
chmod +x ci_scripts/ci_post_xcodebuild.sh

Example

For a complete example integration with Xcode Cloud, see this pull request.

Next Steps

In order to get the full benefits of Screenshotbot we encourage you to integrate the scripts from above into your CI jobs, for example CircleCI or Bitrise.

To start with you could get notifications on Slack each time screenshots change.

Once you're comfortable with that you can then integrate with your Code Review platform of you choice (for example, GitHub), to get build statuses right on your Pull Request.

Ready to get started?

Sign up or contact us.