Writing user interface tests is always a bit complicated, and many developers end up leaving views without testing or dedicating a lot of time and effort to the development of tests. Apple has its own native support for UI testing but does not support view-based testing. This lack of functionality makes many people skip the part of writing tests. Therefore, in Apiumhub we decided to write an article about iOS snapshot testing.
View-based testing means verifying that what the user sees is exactly what we ( as developers ) want them to see. Thanks to this testing we can guarantee that in different states or versions, our views are shown as expected.
The snapshot test takes a capture of a UIView or CALayer and uses the renderInContext method which makes a capture of the view and compares it with the reference image stored in our repository. The test fails if both images do not match and deliver a third capture showing the differences.
The library was created by Facebook (FBSnapshotTestCase) and has now been renamed to iOSSnapshotTestCase and is maintained by uber.
The first step, as they explain on their Github page, is to add the library to our Podfile.
Then we edit the scheme of our target to add the directory where our reference captures will be saved. To do this we add an environment variable with the key FB_REFERENCE_IMAGE_DIR.
Optionally we can also add the key IMAGE_DIFF_DIR to indicate the directory where the differential captures that will be generated, will be saved in case our tests fail.
To see the implementation we will test a simple viewcontroller that will change the visual aspect depending on a state.
The view of the two states:
We configure our test class by making it a subclass of FBSnapshotTestCase instead of XCTestCase.
We define our tests by instantiating the ViewController, assigning the status and calling the FBSnapshotVerifyView method.
First of all, we must execute the tests with the recordMode activated to save the reference images. For this we introduce inside the method of setUp () recordMode = true.
When executing the tests with recordMode activated, Fail will exit. It is normal because you do not have another image yet with which to make the comparison.
Then we comment or eliminate the line of recordMode to launch the tests again and see what happens.
From now on, if the visual aspect of the view changes in the wrong way, our tests will notify us about this issue.
For example, if we inadvertently change the dimension of the icons, the test will generate a differential image with the changes.
I have increased the size of the icon for the 20px test, and the change can be seen in the image.
Thanks to the snapshot testing and to having the uncoupled views, we can instantiate and execute a certain ViewController in the simulator. It is very useful for accelerating the process of views creation and thus reducing development time.
In our didFinishLaunchingWithOptions we check if we are running tests and in that case, we assign the flat and empty UIViewController to the rootViewController.
In our test class, we use an XCTWaiter with a high timeout and assign the controller to the rootViewController to execute.
Finally, in the test we call the debugViewController method.
In this way, we can execute the typical view that is within a long flow of screens of our app in a simple way.
If you are interested in receiving more tips for iOS snapshot testing or movile development in general, I highly recommend you to subscribe to our monthly newsletter here.