Unit Testing — UIView with Nimble+Snapshot

Rodrigo Cavalcante
Cocoa Academy
Published in
5 min readMar 15, 2017

--

Hi everyone, I’m currently working on a series of post that shows how to write some kind of tests for iOS and this is the first one.

In my last post, I talked about TDD and some testing frameworks that helped me a lot when we talk about testing on iOS platform. Here we are going to see how to unit test an UIView created by an XIB file or via code using Cartography.

If you want to learn more about Cartography and view code you can check it out on this post. Now let’s learn about testing!

Set up your project

First, we need to install Quick, Nimble and Nimble+Snapshot on our project. You can do this by adding pod 'Nimble' pod 'Quick' and pod 'Nimble-Snapshots' to your Podfile. Don’t forget to set these frameworks on the test target, if you don’t have one you can create a new in your project. To do that you can select your project -> target -> + -> iOS Unit Testing Bundle and give it a name.

Add test target

Your Podfile should look like this:

Pod file

Using an XIB file

Create your XIB file and its class to look like a login form. Don’t forget to add constraints.

LoginViewXib xib file

Our swift file is just a simple UIView custom class:

LoginViewXib swift file

Now we gonna create a test to make sure our view is cool and our future code doesn’t mess it up.

LoginViewXib test file

We load our view using Bundle.main.loadNibNamed("LoginViewXib", owner: nil, options: nil)?.first as! LoginViewXib .

If you run this test code (Cmd+U) it will fail because recordSnapshot() will always return an error, this method will record a screenshot of your view and fail. After that, you can change it to snapshot() . Finally, you can run you test code again and it should pass.

To make sure your view is cool look at your project’s folder, inside your unit test folder will have a new folder called ReferenceImages and inside this you will see a picture of your view and it should be like:

Recorded snapshot

Using view code

Now let’s create the same test using view code. First, create your new UIView custom class file, add two UITextField and one UIButton and add constraints with Cartography.

LoginView swift file

If you take a better look in this file you will find a closure to be called when the user taps our UIButton and the UIButton will start hidden and only will appear to the user when our UITextFields are filled.

We created an isEmpty() method as an Extension of UITextField to check if our UITextFields are filled with something.

UITextField isEmpty extension

Now let’s write our test code. In this case, we are going to write a few more tests to make sure if our UIButton is appearing when user fill his username and password.

LoginView test file

We created an UITextField extension to help us set the text and send an editingChanged action. If you use username.setText("username") it will not act like a real person is typing in your UITextField because it will not trigger the editingChanged event.

UITextField setTextAndSendEvent extension

In this test file we create our view in a different way that’s because we do not have an XIB file. We do this creating a new object using LoginView() and then calling view.translatesAutoresizingMaskIntoConstraints = false if we do not call this method our test will fail because our view will have a zero (0,0) size. Calling this method makes our view to resize itself based on its constraints.

This test will create four snapshots that should look like theses below: One with our fields empty, another one with only username filled other only with password and the last one with both UITextFields filled and with our UIButton appearing on the screen.

LoginViewTest recorded snapshots

Like our previous test with an XIB, these screenshots will be created in your projects folder. You can check them everytime you want.

How Nimble+Snapshot works?

You’re probably asking me — How I’m going to know when I break my view or what did I break? Nimble+Snapshot create a diff screenshot and a fail one when you break your test. Let’s see this in a practical way.

Let’s comment our textFieldDidChange() method to remove the hide/show behavior.

Comment textFieldDidChange() method

Run you test (Cmd+U) and you will see that a test fail.

failing test

If you take a look at your log (you can filter by png ) Nimble+Snapshot printed a path to images of the failing test. Open Finder -> Go -> Go to Folder (Cmd+Shif+G) and paste the path. The path is something like: /Users/youruser/Library/Developer/CoreSimulator/Devices/adeviceid/data/Containers/Application/anapplicationid/tmp/LoginViewTests/failed_LoginView_should_show_loginButton_if_user_set_username_and_password@2x.png

reference screenshot / failed screenshot / diff screenshot

The folder contains three images: reference, failed and a diff. Based on theses images you can see what you’ve broken in your code and fix it.

Testing a UIView is a good way to know if you break something while coding or even if your code is behaving as expected. As you can see we didn’t need to write a test to verify our show/hide behavior, we check that by snapshotting our view.

If you want to learn more about Snapshot and UITesting you can check out this talk on realm.io.

--

--