Testing View Controllers with Quick and Nimble

View controllers are one of the pillars of your application architecture. They are the place where the user interface connects with the app logic and model. On them it can be sensible pieces of code that if modified could cause a substantial damage, making it an important place to be tested. Although, write tests for view controllers it is still an obscure topic, on this tutorial I will try to clarify that, with an example using Quick and Nimble.

Quick and Nimble

Quick is a behavior-driven development framework for Swift and Objective-C. Inspired by RSpec, Specta, and Ginkgo.

Nimble is a Matcher Framework also for also both languages.

In another words Quick is a framework for creating BDD tests that comes with Nimble that can help to provide more clear expectations for your tests.


The Example

The example will be an iOS app written in Swift 1.2, named Pony. The app has a tab-bar and a view controller as the app intro that will be prompted when the app starts, the Main.storybard look like this:

and running:

Th
  1. Loaded.
  2. Intro is prompted.

Setting up Quick and Nimble on Pony app

If you have the latest CocoaPods version installed, you just have to add them in the Podfile:

CocoaPods: “Pods written in Swift can only be integrated as frameworks; this feature is still in beta…” so you should add use_frameworks!

Quick File Templates (Optional)

You can install using Alcatraz or manually with via the Rakefile

Attention!

Before you start make sure the class is public, e.g:

Creating a test class

If the Quick templates are installed you can just go: create a new file and select quick template and import the app module, e.g:

If you are having trouble importing the app module check this: http://stackoverflow.com/a/24151067/1970675

Testing

PonyTabController: UITabBarController

PonyTabController is a UITabController subclass that is responsible for presenting the app intro if it hasn’t already and dismiss-it if the delegate is called.

Let’s start with our first expectation on viewDidAppear:

“When app intro had never been dismissed it should be set as the appIntroDelegate”

Our first spec:

Now we have the expectation described but there are some things missing, there is no assertion or setup for the object that we want to test (an instance of PonyTabController) and it is not being invoked by any method. In order to setup that more clearly we can divided the test code into three groups: Arrange, Act, Assert, for example:

Arrange:

  • Create a instantiate a PonyTabController.

Act:

  • Trigger the instance of the PonyTabController lifecycle .

Assert:

  • tabController should be the appIntroViewController delegate

resulting:

Now a fully working test ☺

Organising the test code on this way its best known as the: “Arrange-Act-Assert” pattern and some benefits are:

  • Clearly separates what is being tested from the setup and verification steps.
  • Clarifies and focuses attention on a historically successful and generally necessary set of test steps.

Now doing the same for:

When app intro had never been dismissed it should be presented

and

When app intro had never been dismissed and appIntroDidFinish is called it should dismiss app intro

Little brief about “describe” and “context”:

Describe: wrap a set of tests against one functionality

Context: wrap a set of tests against one functionality under the same state.

Concluding the tests for PonyTabController.

You can check the full project here.


Extra

Nimble comes with the function waitUntil where you can execute something inside it closure and call done() when is ready, if done() is not called within a second the test will fail, if you need to extend that interval the function accepts a timeout parameter where you can specify for how long the function will wait until it fails. It could be handy when you want to wait for a view controller to be presented, e.g:


Concluding

  • You can use Quick and Nimble to help you achieve more meaningful tests with clearer expectation.
  • There is a lifecycle that must be followed, i.e: you can’t present a view controller if there another already being presented or the view is not part of the hierarchy.
  • UIKit provide public methods to help trigger view controller states.
  • Testing on view controllers can be tricky sometimes, try to keep them thin as possible ☺

Links

Special thanks

@eldudi and @banaslee