Photo by Jeswin Thomas on Unsplash
Faster unit tests by not running the app startup logic
How to bypass the iOS app when running unit tests
Running unit tests in Swift will launch the (iOS) simulator and the app to which the unit test target belongs. The app probably has some startup logic, e.g asking a remote API for new data, loading information from the local storage, registering for push notifications, etc. These activities are unnecessary when running the unit tests.
it can meddle with the global state, resulting in hard to diagnose failures. Sometimes, it can even make the tests noticeably slower or log noise into your analytics.
I found two insightful articles from Gio Lodi on his blog mokacoding.com about this topic.
- How to bypass the SwiftUI App when running unit tests
- Prevent Unit Tests from Loading AppDelegate in Swift
He also shares detailed examples on GitHub. Below you find code snippets for quick reference.
SwiftUI
Here is the recipe to load a different App
when running unit tests in a SwiftUI project.
// (c) 2016 - 2020 Gio Lodi @mokagio - mokacoding ☕️
// AppLauncher.swift
import SwiftUI
@main
struct AppLauncher {
static func main() throws {
if NSClassFromString("XCTestCase") == nil {
MyAwesomeApp.main()
} else {
TestApp.main()
}
}
}
struct TestApp: App {
var body: some Scene {
WindowGroup { Text("Running Unit Tests") }
}
}
// MyAwesomeApp.swift
import SwiftUI
struct MyAwesomeApp: App {
var body: some Scene { ... }
}
UIKit
Here is the recipe to have a dedicated AppDelegate
when running unit tests in a UIKit project:
// (c) 2016 - 2020 Gio Lodi @mokagio - mokacoding ☕️
// main.swift
import UIKit
private func delegateClassName() -> String? {
return NSClassFromString("XCTestCase") == nil ? NSStringFromClass(AppDelegate.self) : nil
}
UIApplicationMain(
CommandLine.argc,
CommandLine.unsafeArgv,
nil,
delegateClassName()
)
// AppDelegate.swift
import UIKit
class AppDelegate: UIResponder, UIApplicationDelegate {
// ...
}