Snapshot XCUI Testing
This tutorial show you how to use the popular testing library iOSSnapshotTestCase with XCUITests. Feel free to skip ahead if you already know how to create a project and install some pods.
Set up the Project
Then in the root of your project, run pod init
. This will create a Podfile in the root of your project. Open it and add pod 'iOSSnapshotTestCase'
to the Test and UITest targets like so:
Then run pod install
. This will add the required files to those targets.
Next, you’ll want to edit your scheme to add the necessary environment variables.
The first two you can copy paste from the iOSSnapshotTestCase repo, the third will be identical except for the last part of the path.
FB_REFERENCE_IMAGE_DIR
is where the tool will store the reference images.
IMAGE_DIFF_DIR
is where the tool will store the diffs to examine failures.
FAILED_UI_TEST_DIR
is our custom directory where we’ll store the snapshots from our XCUITest failures
Add an XCUITest
Now we need something to test. In Main.storyboard
add a UILabel
with some text. Also add a UIButton
that segues to a new UIViewController
with a different feel. For instance:
In the pre-generated SnapshotUITestingUITests
, replace the generated code with this:
Enable your XCUITest to Take Snapshots
Create a new file, call it SnapshotTestCase
and add the following code:
Next, update your UITest to inherit from SnapshotTestCase
and set it to record your first screenshot.
Almost everything in here is standard usage of the library and you should check out their README for more on how it works. Long story short, the library requires that we pass it a view. The trick here is that we’re using the image property on XCUIScreenshot
to create a UIImageView
. Which is what the library requires to create and store an image for comparison.
Run the test. It should fail with a message reminding you to toggle recordMode
to false.
This is good. If you toggle recordMode
to false (or delete that line), your next run will compare the screenshot against the one it just took. You should have no trouble finding your stored images in your project directory.
Fix the Status Bar Issue
I’ll save you the journey of discovery and just tell you there’s a problem. Doing things this way takes a screenshot of the entire simulator.
Don’t worry. We can fix this. Add a new file to your UITest target and call it UIImageExtensions
. Add the following code.
This will remove a status bar-sized chunk from a UIImage
. We can now add a method to our SnapshotTestCase
class.
This will handle taking the screenshot, cropping it, and calling the requisite method on the library.
Now in your test you can just call verifyView()
Tada!!!!! Now you can replace all those annoying assertions with a simple image comparison!
One More Thing
So while I was using this, I had another idea. What if we could use this to access screenshots of failed XCUITests that are run in CI?
Here’s the caveat. If you’re just running tests for yourself, you probably don’t need to set up screenshots on failure. You can already access a test report in Xcode that has screenshots of the failure. However, there are a few scenarios I can think of where you would want this extra functionality.
- you’re running your tests headless in parallel
- you’re running your tests in continuous integration and you don’t have ready access to the build and test logs
- you’re running your tests in continuous integration and you DO have ready access to the build and test logs but you work with a lot of people so finding the logs you care about is really annoying
So if any of that applies to you, stay with me. In SnapshotTestCase
we’ll override the recordFailure
method.
And before we go any further, let’s break that test we wrote earlier.
It won’t find “Hello World”. This will dump us into the recordFailure
method on SnapshotTestCase
. Let’s add a one more method to be able to take and save a screenshot.
- Takes a screenshot using the normal mechanism and converts it to Data
- Creates a path for saving screenshot that includes the name of the test that failed and the line it failed on
- Uses the environment variable we stored earlier to determine where to store the image
- Creates the necessary directory if it doesn’t already exist
Now when you run the test and it fails, you should see the failure stored in the directory you specified.
Conclusion:
Haven’t used this much yet but it seems really promising. Let me know what you think and if you wind up using it! You can find the completed project here: https://github.com/joesus/SnapshotUITesting