Let us discuss how to improve the test case execution using TestAppDelegate in this article:
Unit testing should be as fast as possible and anything that slows it down should be removed.
We have a more than 1K test cases in our application. As test cases got increased it slowed down running tests. I had a discussion about this with my colleagues and captured two words from discussion i.e. Set none to host application
or use TestAppDelegate
. I did research on host application
and came to know that it is must to set for application tests. We have a tests in other target i.e. cocoa touch framework(library) that doesn’t need main application as host to run tests and it works perfectly there. Till now host application
point is clear.
Testing Sequence goes like this:
1. Launch the simulator
2. In the simulator, launch the app
3. Inject the test bundle into the running app
4. Run the tests
Whenever test case runs, it invokes main.m which invokes appDelegate heavy code. It’s good to use TestAppDelegate
when test runs. It should be set up when we start new project to avoid any side effect as app delegate will grow over time. Unfortunately, we are going to use TestAppDelegate
now.
TestAppDelegate
?When iOS launches an app, it needs one of the following things:
@UIApplicationMain
notation: it’s applied to a class to indicate that it is the application delegate. UIApplicationMain()
function: it’s called in the main entry point—which is usually main.swift
—to create the application object, the application delegate and set up the event cycle.By default, an iOS project has a class AppDelegate
with the notation @UIApplicationMain
. It means that this class is the entry point of your app.
Then, iOS has to find the entry point for the UI. We can use two different ways to load the main UI component: either Using Storyboard
or Load Programmatically
.
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
window = UIWindow()
window?.rootViewController = UIViewController()
window?.makeKeyAndVisible()
return true
}
}
First of all, we need a new entry point to load either the normal or the test AppDelegate
depending on whether the app is launched by unit tests or not.
The entry point of an app can be either an AppDelegate
with the notation @UIApplicationMain
or the UIApplicationMain()
function.
The first step is remove the @UIApplicationMain
annotation from AppDelegate.swift, create a new file main.swift
. In this file we have to check if the app is launched by unit tests:
let isRunningTests = NSClassFromString("XCTestCase") != nil
Now, we have to decide which app delegate class to load:
return isRunningTests ? NSStringFromClass(TestAppDelegate.self) : NSStringFromClass(AppDelegate.self)
Instead of TestAppDelegate
nil can be also return.
return isRunningTests ? nil : NSStringFromClass(AppDelegate.self)
Final main.swift
looks like this:
import Foundation
private func delegateClassName() -> String? {
return NSClassFromString("XCTestCase") == nil ? NSStringFromClass(AppDelegate.self) : NSStringFromClass(AppDelegateStub.self)
}
UIApplicationMain(CommandLine.argc, CommandLine.unsafeArgv, nil, delegateClassName())
We know that the TestAppDelegate is called just once and before the unit tests. It means that you have the possibility to run test logic in your TestAppDelegate once and before running the set of unit tests.
import UIKit
class TestAppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
}
We don’t need to assign view controller to window here.
If you are using any ui test or snapshot framework which needs test to be hosted by an application with a key window then you need to use below test app delegate.
import UIKit
class TestAppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
override init() {
super.init()
window = UIWindow(frame: UIScreen.main.bounds)
window?.rootViewController = UIViewController()
window?.makeKeyAndVisible()
}
}
After this change all tests got passed in improved time.
If you use core data in app delegate and want to initialize there is a good article to follow: Fake AppDelegate For Unit Testing In Swift
We’ve reduced the app launch step of testing to a bare minimum. It’s good to take this step early while setting up project because your real iOS app delegate will eventually grow.
Thanks for reading article.
This is a free third party commenting service we are using for you, which needs you to sign in to post a comment, but the good bit is you can stay anonymous while commenting.