This episode is for subscribers only. To access it, and all past and future episodes, become a subscriber today!See subscription optionsorLog in
Sign up for our weekly newsletter to be notified of new episodes, and unlock access to any subscriber-only episode of your choosing!Sign up for free episode
Previously we refactored a small snapshot testing library that worked with images of
UIViews and we generalized it to work with even more types by using protocols. This first let us write tests against values of any type that could produce an image! We conformed
UIViewController and instantly unlocked the ability to write snapshot tests against them. None of these types worked with our original API.
We then generalized it even further to work with any kind of snapshot format, not just images! This gave us the ability to write snapshot tests against blobs of text, and by the end of the episode we had a truly impressive, flexible library that we could have continued to extend in interesting ways.
However it wasn’t all roses. We hit a bunch of protocol-oriented bumps along the way: we butted heads with complicated features like capital
Self requirements, dodged naming collisions, and recovered some ergonomics by way of protocol extensions and default conformances (oh my!). We finally hit the ultimate bump that stopped us in our tracks: types can only conform to protocols a single time, so our library is limited by the language to a single snapshot format per type. This may not seem like a big deal. As a community we do so much with protocols and they really don’t seem like a limiting factor in our APIs. However, we saw that even the type we started with,
UIViews, had multiple useful conformances, including screen shots of how the views render and text-based descriptions of the view’s state and hierarchy.
Luckily, over the past weeks we’ve built up the muscles to convert protocols to concrete datatypes and functions, and we’ve seen firsthand how it helps us solve this multiple conformances problem! So let’s re-refactor our library and see what plain ole datatypes and functions can do for us.
Take our witness-oriented library and define some interesting strategies! Think about your own code base and specialized
Diffing) instances you can define. Here are some suggestions to get you started!
dump strategy on
Snapshotting<Any, String> that uses the output of Swift’s
dump function. You can reuse logic from the
recursiveDescription strategy to remove occurrences of memory addresses.
Snapshotting<URLRequest, String> strategy that snapshots a raw HTTP request, pretty-printing the method, headers, and body of the request.
Snapshotting<NSAttributedString, UIImage> strategy that snapshots images of attributed strings.
Snapshotting<NSAttributedString, String> strategy that snapshots HTML representations of attributed strings.
Apple’s eponymous WWDC talk on protocol-oriented programming:
At the heart of Swift’s design are two incredibly powerful ideas: protocol-oriented programming and first class value semantics. Each of these concepts benefit predictability, performance, and productivity, but together they can change the way we think about programming. Find out how you can apply these ideas to improve the code you write.
Facebook released a snapshot testing framework known as
FBSnapshotTestCase back in 2013, and many in the
iOS community adopted it. The library gives you an API to assert snapshots of
UIView’s that will take
a screenshot of your UI and compare it against a reference image in your repo. If a single pixel is off
it will fail the test. Since then Facebook has stopped maintaining it and transfered ownership to Uber.
Stephen gave an overview of snapshot testing, its benefits, and how one may snapshot Swift data types, walking through a minimal implementation.