Introduction -

Swift 2.0 introduces the concept of Testability. Testability allows us to write tests of Swift 2.0 frameworks and apps without having to make all of their internal routines public.

Before Testability, testing internal routines and modules (i.e. routines and modules that are out of access control scope) directly in Swift was impossible. This often leads us to exposing properties and routines as public or adding them to the test target solely for the purpose of being able to test them. Obviously, changing program design and implementation just to be able to utilize unit testing is not ideal and defeats the purpose of constructing application accesses that way in the first place.

With Swift 2.0, Testability makes unit testing all testable interfaces easy. This article will provide insight into Swift 2.0 Testability by example.

Testability -

To exhibit Testability and testing in Swift 2.0 I created a simple, single-view, project that reads an RSS feed of recent earthquake data and presents that data to user. The project can be found here.

Let's examine a class in the EarthQuake module that is responsible for retrieving and accessing earthquake data for the application. The class, EarthQuakes, has a single, interface responsible for presenting earthquake data to it's caller. The getEarthQuakeData function, which comprises this interface, is defined as an internal instance function. Internal access is the default behavior and enables entities to be used within any source file from their defining module, but not in any source file outside of that module.

 /**
 * Gets earthquake data from RSS feed with completion.
 *
 * @param days - the number of days for which earthquake data should be retrieved
 * @param completion - closure with inner closure that will return results or throw error
 */
 func getEarthQuakeData(days: Int?, completion: ((inner: () throws -> [EarthQuake]?) -> ())) {

 if let url = NSURL(string: EarthQuakes.quakeURL) {

 let request = NSURLRequest(URL: url) //request to earthquake data
 let session = NSURLSession.sharedSession()

 //send request to get earthquake data asynchronously with completion
 session.dataTaskWithRequest(request, completionHandler: { [weak self] (data, response, error) -> Void in

 if let strongSelf = self {

 //throw error in completion upon error
 if let error = error {
 completion(inner: {throw error})
 return
 }

 //check for status error in response and throw error if found
 if let response = response, httpResponse = response as? NSHTTPURLResponse {
 if httpResponse.statusCode < 200 || httpResponse.statusCode > 399 {
 completion(inner: {throw QuakeError.StatusCodeError(code: httpResponse.statusCode)})
 return
 }
 }

 //parse earthquake data
 if let data = data {

 //reset earthquake list
 strongSelf.earthQuakes = [EarthQuake]()

 //setup xml parser and start parse, throw error if parse error occurs
 let parser = NSXMLParser(data: data)
 parser.delegate = self
 if !parser.parse() {
 completion(inner: {throw QuakeError.ParseError})
 return
 }

 //trim and sort list of earthquakes
 strongSelf.trimAndSortEarthQuakes(days, order: .OrderedDescending)
 }
 //call completion 
 completion(inner: {return strongSelf.earthQuakes})
 }
 }).resume()
 }
 }

Internal functions fall into realm of routines and modules which, as previously mentioned, are not accessible to be tested unless we take specific action to make them so.

In Swift 2.0, with Testability, the @testable attribute is introduced. This attribute allows a test target class to have the same access to an application's interface(s) as any other module in the application would. No longer do application classes need to be added to test target or routines be made public just to test. The syntax of attribute is @testable import MODULE_TO_TEST and can be seen in the sample projects EarthQuakeTests.swift class.

import XCTest
import OHHTTPStubs
@testable import EarthQuake

class EarthQuakeTests: XCTestCase {

 let timeout = NSTimeInterval(3.0)

 override func setUp() {
 super.setUp()
 }

 /**
 * Removes stubs after each execution of a test
 */
 override func tearDown() {
 super.tearDown()

 //remove all stubs on tear down
 OHHTTPStubs.removeAllStubs()
 }

...TESTS
}

With the @testable attribute nothing more is needed in order to make calls to the EarthQuake application's internal interfaces for testing!

Test Example -

EarthQuakeTests.swift has a number of unit tests desinged to cover the EarthQuakes class. Each test accesses the EarthQuakes class via the internal getEarthQuakeData function. As defined above, this function takes a number of days and a closure. The days parameter is used to filter out all results that fall outside of a given range from today. The closure contains an inner closure defined as throwable that returns an optional array of EarthQuake objects. This is done so that an exception can be thrown within the completion block of an asynchronous call. The call itself asynchronously retrieves earthquake data from U.S. Geological Survey feed. To test against the remote calls I used the OHHTTPS stubbing library. The following is an example test from project:

/**
* Tests getting earth quake for EarthQuakes data against stubs with 30 days of expected data.
*/
func testGetEarthQuakeDataSuccessAt30() {

 //set up stub to use
 stub(isHost(EarthQuakes.quakeURL), response: fixture("EarthQuakeStubSuccess.xml"))

 let trimEarthQuakeDays = 30
 let expectedEarthQuakes = buildEarthQuakeExpectedList()
 var asynchEarthQuakes: [EarthQuake]?

 //get earth quake data
 let responseArrived = self.expectationWithDescription("Response of async request has arrived.")
 EarthQuakes.sharedInstance.getEarthQuakeData(trimEarthQuakeDays) { (inner) -> () in

 responseArrived.fulfill()
 do {
 asynchEarthQuakes = try inner()
 } catch _ {
 //fail test
 XCTAssertFalse(true)
 }
 }

 //wait for asynchronous call to complete before running assertions
 self.waitForExpectationsWithTimeout(timeout) { _ -> Void in

 //test assertions
 XCTAssertEqual(asynchEarthQuakes!.count, 11)

 for x in 0..

In this test I set up a stubbed response corresponding to the request, set up an expected results list (based on the stubbed response), execute the call to getEarthQuakeData, and then make assertions based upon what behavior should be. Because of the asynchronous nature of the data call, I set up an XCTestExpectation with a timeout and fulfill it when the data comes back (when the given closure is executed), this way we ensure that the assertions are executed only once asynchronous call has returned.

Xcode 7 -

With Xcode 7 comes code coverage analysis. After executing any set of test cases, you can now view the coverage produced by those tests. To do this:

  • Ensure code coverage is turned on - Edit current schema, select test item, and ensure that Gather coverage data is selected.
  • Execute tests - (⌘+U)
  • Go to report navigator to view results

As you can see by the coverage meter there is full coverage over all branches in the EarthQuakes class and incomplete coverage over all branches in the EarthQuakeViewController class. A smart feature of Xcode 7 comes when you click on the encircled arrow beside of class name. In doing so, Xcode will navigate to the source code that was tested and highlight where tests coverage was applied and where test coverage was not applied as well as numbers indicating how many times a statement was executed during testing.

Conclusion -

Swift 2.0 introduces a number of useful practices for making the testing of IOS apps more intuitive. Additionally, Xcode 7 provides some exciting new tools that will make managing tests in applications easier. With Testability and code coverage, our test projects can be created easily and the strengths and deficiancies in our applicaiton tests are immediately apparent.

As a note, there is still no straight forward way to test private methods with Testability. This seems to be done with purpose as classes should be tested via their interface API. The API defines a contract, which is a well-defined set of expectations about how our application will to act based on different inputs. Abiding by the contract, if a class contains private methods then these can be tested through it's accessible API.