Swift - Unit testing functions that involve IBOutlets?

You have to initiate the view controller using the storyboard. See the documentation here: https://developer.apple.com/library/ios/documentation/uikit/reference/UIStoryboard_Class/index.html#//apple_ref/occ/instm/UIStoryboard/instantiateViewControllerWithIdentifier:

If you initialize the view controller directly, it will not have any connections because the VC itself does not know of the storyboard in this case.


Testing IBOutlet's is not the best approach, because:

  • outlets should be considered implementation details, and should be declared as private
  • outlets are most of the time connected to pure UI components, and unit tests deal with testing the business logic
  • testing the value injected into the outlet by another function can be considered somewhat as integration testing. You'd also double the unit tests you have to write by having to test the connected/unconnected outlet scenarios.

In your particular case, I'd recommend instead to test the validator function, but first making it independent of the controller class (if it's not already). Having that function as an input->output one also bring other benefits, like increased reusability.

Once you have tested all the possible scenarios for the validator, validating that the outlet correctly behaves it's just a matter of a quick manual testing: just check if the outlet behaves like the validator returned. UI stuff are better candidates for manual testing, as manual testing can catch other details too (like positioning, colors, etc).

However, if you really want to test the outlet behaviour, one technique that falls into the unit testing philosophy is snapshot testing. There are some libraries available for this, I'd recommend the one from https://github.com/uber/ios-snapshot-test-case/.


Try this code to initialize the IbOutlets of your view controller:

   let yourStoryboard = UIStoryboard(name: "Your_storyboard", bundle: nil)
   yourViewController = yourStoryboard.instantiateViewController(withIdentifier: "YourViewController") as! YourViewController
   yourViewController.loadView() // This line is the key

You may need to add the controllers view to a hierarchy prior to testing to force the controller to load the XIB

let localContainer = UIView(frame:someFrame)
let controllerUnderTest = //instantiate your controller
localContainer.addSubview(controllerUnderTest.view)

//at this point you can test outlets

Otherwise your outlets will be nil as they haven't been connected yet.