Here’s a thing that all iOS developers know, but (usually) don’t say out loud: Testing your views on all of the possible iOS screen sizes is exhausting. XCUITests are dreadfully slow, and screenshot tools are painful to update and error prone. So we’re left with one option: Unit testing our views.
What you’ll need.
- A programmatic view controller.
If your view controller is not programmatic you won’t be able to instantiate it with a particular frame. Loading your view controller from a storyboard or XIB will limit you to the simulator you are running that test on.
- A good attitude.
What’s our end game?
We want to be at a place where making small view changes doesn’t give us a stomach ache. And, arguably more importantly, we want to be able to easily and quickly unit test our views on all screen sizes.
1. Create your helpers.
a) View controller factory.
You will likely want to test more than one view in this view controller, so you’ll need an easy way to spin the view controller you intend on testing.
b) Define device dimensions.
It’s essential to be able to test all screen sizes.
2) Test a particular frame
Let’s just take a second and unwrap what’s going on here:
We want to know that no matter the shenanigans are going on around the body view, it will stay put (vertically). We iteratively use our
safeAreas and our factory to create our
bodyView and compare its frame to that of its superview (in this case the view controller’s view).
Keep in mind that this example is very simple. If you need to look at a view’s position relative to any of its superview’s superviews, take a look at Paul Hudson’s article on How to convert a CGPoint in one UIView to another view using convert().
So the tests are written, everything is passing, and next week you change one single margin and…the tests fail. Well great.
Let’s set a breakpoint and see what it looks like:
Not only is the car safe, but it feels good to drive. Moving us from fragility to stability in one fell swoop. We have written a test that makes sure that our views stay where we want them and gives us an extremely powerful tool in our arsenal when debugging them.
Where to go from here
How you want to approach your view tests is really up to you and what the needs of your view are. Don’t go down a rabbit hole of testing every frame of every view. Test only what is needed. The example test above was testing vertical distance because the in-call view tends to be finicky when modifying heights of the various views that compose it. There would be no point for us to test things like the width of the body view, because even if it did change, it would have no impact on any other view as there are no horizontally related views to the body view.
One last PSA, at TextNow we have created our own injectable device as our constraint constants are modified slightly for the iPhone SE/5s. This is a great example of setting your view/view controller up in a manner that is injectable. For some info on how to do this with mutable structs have a watch of Stephen Celis’ talk on How To Control The World.
“Its so great and I never knew it could be this powerful!”
— Quinton Pryce, circa September 2020
“love love love love love it”
— Anonymous, ibid.