Testing a View Controller's IBOutlet Connections
When Using Nib Files
Understanding how a ViewController's view hierarchy is loaded is essential when testing UI related stuff. Consider the following test case, which verifies that an IBOutlet connection to myLabel
has been established when a view controller is loaded:
The test will always fail, telling you that vc.myLabel
is nil. There is a very good reason for that. Apple's View Controller Programmer Guide for iOS illustrates a view controller's loading and unloading sequence with the following diagram:
It is described as follows:
Whenever some part of your app asks the view controller for its view object and that object is not currently in memory, the view controller loads the view hierarchy into memory and stores it in its view property for future reference.
This explains why myLabel
is always nil in the test case above; it has not yet been loaded, since - at the time STAssertNotNil
is reached - the view property has not yet been accessed. To force loading of the view hierarchy, invoke the view
property of the viewcontroller:
Now, as opposed to before, the test passes, because the view controller loads the view hierarchy into memory and stores it in its view property
When using Storyboards
When using storyboards, initializing a viewcontroller under test with initWithNibName:bundle:
or init
no longer makes sense, since the views are now embedded in a storyboard, hence no longer accessible via a nib file. To be able to load a viewcontroller realistically when using storyboards, follow these two steps:
- Assign an identifier string to the viewcontroller in the storyboard.
- Load the storyboard programmatically and ask it for a viewcontroller with a desired identifier.
Assume a storboard containing the view of MyViewController
. First, in the Storyboard, select MyViewController
and, in Utilities View, select the Identity Inspector. In here you will find an Identity Setting named Storyboard ID
. This is used to identify the viewcontroller. Assign a unique, descriptive value, eg MyViewController
.
Now, when initializing MyViewController
from test code, follow this pattern to load it programmatically (where Storyboard is the name of the containing storyboard file):
With this approach you don't have to invoke the view property, since the view hierarchy has already been loaded into memory at this time.
[ unit test viewcontroller iboutlet ]