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:
- Remove
@UIApplicationMain
from your existingAppDelegate
class, - 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()
}