read

If you do run unit tests, here’s 1 useful tip to make your tests run faster, and more predictable.

By default, Xcode will run your app while it performs unit testing. But it is unnecessary to run the app because:

  • It wastes time running the app
  • It introduces a dependency
  • It can cause random test failures

What’s the problem?

Let’s use an example where you have a test case that involves networking. When the app is launched, it has a boot sequence and it may make the same request as the test. Depending how resilient you write the test case, it might sometimes fail.

That’s because there will be side effects from a running app. It can change state unexpectedly.

You might have networking mocks, but a running app can still cause havoc when it makes a mock requests. Mock is great, but it does not solve the problem here.

Solution: Don’t launch the app

A simple way to disable app launch is to set the Host Application to None. But that (usually) doesn’t work because most likely you will be using types from the app.

The better solution is to add main.swift and take control of what to run. Do this:

  1. Remove @UIApplicationMain from your existing AppDelegate class,
  2. Then add a new file main.swift
private func delegateClassName() -> String? {
    if NSClassFromString("MyAppUITests") != nil { // UI Testing
        return NSStringFromClass(AppDelegate.self) 
    } else if NSClassFromString("XCTestCase") != nil { // Unit Testing
        return nil
    } else { // App
        return NSStringFromClass(AppDelegate.self)
    }
}

UIApplicationMain(CommandLine.argc, CommandLine.unsafeArgv, nil, delegateClassName())

main.swift is the entry point for an application. The code above simply initialize UIApplicationMain with different delegate. Yes, you may provide an entirely different delegate, or nil!

When running unit test target, it uses a nil delegate, therefore it does not run the usual app sequence.

If you do run UI Test target, then you would want to change “MyAppUITests” string to a class you have. You may even return another delegate! This is useful when you are taking screenshots and want a certain boot sequence 🚀

For SwiftUI App

It is similar for SwiftUI. But the new framework did away with AppDelegate, and has a simplified main func. You simply call MyApp.main() in main.swift to start an app.

Create an EmptyApp implementation that does nothing. Then run in main.swift accordingly.

if NSClassFromString("XCTestCase") != nil { // Unit Testing
    EmptyApp.main()
} else { // App
    DualgramScreenApp.main()
}

Image

@samwize

¯\_(ツ)_/¯

Back to Home