One of the announcements that excited me most during last year's WWDC was Xcode's built-in support for code coverage. As of Xcode 7, you no longer have to jump through a bunch of hoops to find out how well your test suite has covered your code.
Code coverage is not enabled by default, though. In this tutorial, I show you how easy it is to enable code coverage for a project in Xcode and I also highlight a few benefits of having code coverage enabled.
Project Setup
Fire up Xcode 7, create a new project based on the Single View Application template, and name it Covered. Set Language to Swift and check Include Unit Tests at the bottom to have Xcode generate a test target for us.
Writing Unit Tests
Create a new Swift file by choosing the Swift File template from the iOS > Source section and name it Person.
Open Person.swift and declare the following structure.
struct Person {
let first: String
let last: String
var fullName: String {
return "\(first) \(last)"
}
}
Writing unit tests for Person shouldn't be too difficult. Right? Create a new file by choosing the Unit Test Case Class template from the iOS > Source section and name the file PersonTests. Set Subclass of to XCTestCase and Language to Swift.
Make sure to add the unit test case class to the CoveredTests target.
Testing the Person structure is pretty easy.
import XCTest
@testable import Covered
class PersonTests: XCTestCase {
override func setUp() {
super.setUp()
}
override func tearDown() {
super.tearDown()
}
func testInitialization() {
let person = Person(first: "Bart", last: "Jacobs")
XCTAssertNotNil(person)
XCTAssertEqual(person.first, "Bart")
XCTAssertEqual(person.last, "Jacobs")
}
func testFullName() {
let person = Person(first: "Bart", last: "Jacobs")
XCTAssertEqual(person.fullName, "Bart Jacobs")
}
}
Select the Covered scheme and choose an iPhone simulator from the list of simulators. Select Test from Xcode's Product menu or hit Command + U. Open the Test Navigator on the left to make sure every test passed.
Enabling Code Coverage in Xcode
Code coverage is enabled in the scheme editor. Click the Covered scheme and choose Edit Scheme.... Select Test on the left and check the checkbox Gather coverage data. That is it.
To collect code coverage data, run the test suite one more time. To see the code coverage report, open the Report Navigator on the left, select the report for the last test run, and open the Coverage tab at the top.
You can see that Person.swift is completely covered by the unit tests we wrote. What I like most about Xcode's code coverage is the integration in the source editor. Open Person.swift and add another computed property as shown below.
var fullNameLastFirst: String {
return "\(last) \(first)"
}
Run the test suite again and revisit Person.swift. On the right of the source editor, you see a gutter that indicates how many times the unit tests entered a particular code path. Colors indicate which code paths have not been entered, giving you a clear hint where the test suite is lacking coverage.
Be Careful
Xcode's code coverage tracks which code paths are executed by the test suite and which are not. This can result in false positives. If you inspect the code coverage report, you notice that it says ViewController.swift and AppDelegate.swift are also partially covered by the test suite. But we didn't write any unit tests for these classes.
To run the test suite, Xcode creates an instance of the application, which means an instance of the AppDelegate
class is initialized. The same goes for the class of the initial view controller of the storyboard, ViewController
. That is why the code coverage report indicates that AppDelegate.swift and ViewController.swift are partially covered by the test suite.
What's Next?
Code coverage is a fantastic addition to Xcode. I really like the fact developers no longer have to use third party solutions and the integration in the source editor is the cherry on the cake.
Questions? Leave them in the comments below or reach out to me on Twitter. You can download the source files of the tutorial from GitHub.