iOS Simulator agnostic snapshot testing

  • Scope:
  • Mobile Development

With a debut of each shiny new device equipped with higher resolution screen, developing and testing user interface of iOS applications becomes more and more demanding and time-consuming. For each view, that represents only a small part of an application, there could be several edge cases and states depending on data it works with. In contrast to domain logic code, it’s not obvious how to properly test view’s visual aspects.

It’s been a while since Facebook engineers published an internal tool for UIViewCALayer snapshot testing – FBSnapshotTestCase (if you’re not familiar with this tool, I encourage you to check it out!). The presented approach simplifies testing multiple configurations of view, as well as prevents from introducing unexpected changes in user interface.

Size matters 🙂

When it comes to view controllers, we usually want to ensure they keep visual quality on a set of various existing devices in each supported orientation. This can be achieved by running a test suite on numerous variants of iOS simulators. Such approach will result in extending total test suite’s execution time with multiple simulator’s restarts. Paying so high price for each UI code change is definitely not acceptable during a development process. To tackle this inconvenience we developed FBSnapshotTestCase extension, with a special love for testing view controllers in their natural habitats.

Meet SnappyTestCase

Snapshot tests built with SnappyTestCase don’t depend on a context of simulator or device they run on. Instead, context is defined for each verification statement you use to validate or record a snapshot. Context is specified by passing a set of objects adopting Snap protocol, that defines test environment parameters like device identifierframe sizescreen scale and orientation. For convenience, we prepared a set of objects defining real life iOS devices, ready to be used in your tests. Here we’re testing ExampleViewController with Snap representing the context of the 4″, portrait mode iPhone:

As a result of running the following test, reference image named testExample_iPhone 4,0"_Portrait@2x.png will be recorded and stored on a disk.

Virtual device rack

We gathered and grouped by device type all existing variants of iOS devices into a struct called DeviceRack. It simplifies defining sets of devices you want to simulate in your tests. You can compose device sets by picking test devices one by one, or by using simple chainable API:

  • portrait, landscape – define orientation of single, or sequence of devices,
  • uniqueWidths, uniqueHeights – filter sequence of devices to not contain duplicated widths or heights.

Some real-life usage examples:

ExpressionDevice set descriptionSnapshot count
DeviceRack.iPhone.all.landscapeAll iPhones, in landscape orientation4
DeviceRack.iPhone.all.portrait.uniqueWidthsSet of iPhones covering all possible screen widths in portrait orientation3
DeviceRack.iPad.retina.landscapeRetina 9.7″ / 7.9″ iPad, landscape1

Try it yourself

Described tool is, of course, open sourced 🙂 – code and installation information can be found on our GitHub. We’ve prepared a demo application with some example tests and corresponding reference images. Feedback and contributions are welcome!

Read also about Apple app clips, new feature for iOS 14.