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.

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 {
  // ...
}

Did you find this article valuable?

Support Marco Eidinger by becoming a sponsor. Any amount is appreciated!