Awhile back I blogged about how to test storyboards by loading the ViewController from a Storyboard. Recently I’ve been trying to figure out how to do the same thing in Swift.
Update: latest code appears to be working ok for testing view controllers as long as I don’t try to run them on an actual iPhone device. When I do that, the test hangs.
The code and instructions posted by Mike Cole appear to almost work in Swift with the latest Xcode (I’m using Xcode 6.2, but 6.1 works also). I had to also reference the ViewController’s view. Evidently the ViewController will lazy load the view, meaning that the outlets don’t become connected until after ViewController.view is referenced.
Also, be careful what you put into your view controller’s viewDidLoad method. Code that requires that the view is actually being displayed should be moved to viewWillDisplay or may cause the unit test to crash (since there isn’t really a display during unit testing).
Example XCTest setup code to load a viewcontroller named “MainVC” from main.storyboard.
override func setUp() {
super.setUp()
let storyboard = UIStoryboard(name: "Main", bundle: NSBundle(forClass: self.dynamicType))
vc = storyboard.instantiateViewControllerWithIdentifier("MainVC") as ViewController
let dummy = vc.view // force loading subviews and setting outlets
vc.viewDidLoad()
}
I’ve left the previous post below, but it’s basically outdated now. After running into various problems, I resorted to using Objective-C unit tests, and describe how to do so below. I wouldn’t recommend doing so anymore, since Swift tests appear to work ok now.
After fumbling around for awhile with the Swift test case file, and not making much progress, I decided to try something different, and tried adding an Objective-C test case file to my Swift project. At least in this case, I know how to load the ViewController from the Storyboard. And this actually worked.
This turned out to be fairly simple. Let me warn you though, that I consider this approach to be a temporary hack, albeit an interesting one. I believe the correct solution will be to figure out how to do this in a Swift unit test case file. At this point though, I’m not sure if I’m being prevented from doing so due to bugs in beta level Xcode 6, or just my lack of Swift knowledge.
But caveat emptor aside, here is how to create an Objective-C unit test case file in an otherwise entirely Swift project, and use it to load and test a ViewController from a storyboard:
- Set the product module name so that you will know the swift header file name to import (eg. “#import ProductModuleName-swift.h”). Note that this should not be necessary, as the Product Module Name is supposed to default to the Product Name. This wasn’t working as of Beta 2.
- Add an Objective-C unit test case file. Make sure to set the language to Objective-C. It will default to Swift.
- I did not have to create an Objective-C Bridging Header for the Swift ViewController, because I’m only going the other way, from Swift to Objective-C, but I did anyways when prompted. I don’t think having it will hurt anything, and you might want it later.
- Import the swift header file into the Objective-C unit test case file:
#import “ProductModuleName-swift.h”
That was it. This then gave me access to my ViewController definitions. Then use the previous described method of loading a storyboard and viewcontroller from it.
I will update this article once I figure out how to do it correctly in a Swift unit test case file.