The natural next step from testing your code, perhaps even in a test-first manner, is knowing how much of your code is covered by these tests. To do that, you have to first open the Edit Scheme
and tell Xcode to Gather coverage data
1.
Running unit tests as is would give you inaccurate coverage, as it would take into account AppDelegate
and everything it touches2. This is easily seen on a new project, using the Single View Application
template that comes with AppDelegate.swift
and ViewController.swift
files. As you see below, I've also added AnIntegration.swift
that contains only one empty function which gets called from AppDelegate
and so, by definition, has a 100% coverage:
To get accurate code coverage first add a AppDelegate
barebones replica to your test-target:
import UIKit
class TestAppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
return true
}
}
Then add main.swift
to the app-target in and finally tell Xcode to, when testing, replace AppDelegate
with the above TestAppDelegate
:
import Foundation
import UIKit
let argc = CommandLine.argc
let argv = UnsafeMutableRawPointer(CommandLine.unsafeArgv).bindMemory(to: UnsafeMutablePointer<Int8>.self, capacity: Int(argc))
let appDelegateClass: AnyClass = ProcessInfo.isUnitTesting
? NSClassFromString("MyAppTests.TestAppDelegate")!
: AppDelegate.self
UIApplicationMain(argc, argv, nil, NSStringFromClass(appDelegateClass))
The ProcessInfo.isUnitTesting
above is an extension on ProcessInfo
:
import Foundation
extension ProcessInfo {
static var isUnitTesting: Bool {
return processInfo.environment["XCTestConfigurationFilePath"] != nil
}
}
Since you supplied main.swift
file, you now also have to remove the @UIApplicationMain
attribute that's right above your AppDelegate
:
@UIApplicationMain // remove this
class AppDelegate: UIResponder, UIApplicationDelegate {
Finally, run the tests again and coverage of AnIntegration
is now at the actual value of 0%:
-
This setting is currently (Xcode 8.2.1) disabled by default. ↩
-
Because app process hosts these unit tests and they are executed after receiving notification for
UIApplicationDidFinishLaunching
. ↩