Ah, iOS Unit Tests. We all know we should write them, and yet so few of us do. Perhaps one reason we don’t is because sometimes simply setting up iOS Unit Tests is an exercise in patience itself. I encountered this recently when I wanted to add a few tests for some date logic in an app I’m currently working on. I thought I could simply add in a test case class for the one module, run the tests, and everything would be just fine. I was sorely mistaken.

The app I’m working on uses Realm as a database (which is totally awesome you should use Realm!). It works perfectly fine when I launch the app in a normal usage mode. However, when I launch the app as part of a test target, it crashes when the first view controller appears. I could have put a bunch of time into making Realm work on my test target, but I’m kind of lazy. I wanted a simple way to bypass the launch of the original ViewController. Then I could add only the tests that I’m interested in. Here’s how I did that.

Step One To Easy iOS Unit Tests – Bypass The App Delegate

I wasn’t aware of this, but there is a way to get your app to recognize that it’s being run on a test target. You can use this logic to override your App Delegate and replace it with a fake one that swaps out your initial View Controller with a dummy. You do this by creating a new Swift file called “main.swift” and placing some code like the following in that file.

So what does this do? It determines if you are in a unit testing mode, and then it swaps your AppDelegate class with a different class that you specify. In this case, I have chosen to create a totally brand new TestingAppDelegate class that only runs when I’m running my iOS Unit Tests. When you do this, you will notice a new error pop up on your AppDelegate class, right at the line where you specify @UIApplicationMain. This happens because Xcode recognizes that there are two entry points to the main application. Simply delete the @UIApplicationMain line, and you’ll be good to go. The main entry to your app will be handled by the new main.swift file.

When you roll your own main.swift implementation, you can no longer use the @UIApplicationMain directive. That's okay! Just get rid of it.

When you roll your own main.swift implementation, you can no longer use the @UIApplicationMain directive. That’s okay! Just get rid of it.

The TestingAppDelegate

This new TestingAppDelegate will be quite a lot like your ordinary App Delegate, except you can stub out the Root ViewController at launch time, thus making your iOS Unit Tests much more lightweight. Here’s how I did that.

TL;DR – It’s the same AppDelegate, except we set a blank UIViewController as the window’s rootViewController right before the application launches. Now when you run your tests, you’ll no longer see a white screen but a black screen. You’ve now made your unit tests independent of the first ViewController in your App! You can test separate modules that don’t rely on the initial view controller. This is a really helpful approach to take when you’re working in a team environment and you don’t have the time to get all of the initial view controller tests up and running. It lets you build your tests in a more piecemeal fashion. Then, once you’ve convinced your manager that the tests are worth the extra time, you can go back in and do it properly.