Swift Unit Testing in Xcode

In this blog, we’re going to look at creating a simple app to show how easy it is to add unit testing to Swift apps in Xcode. We’ll look at how much we get out of the box and then create a calculator app to do some simple unit tests.

First create a new Xcode project. We want it to be a Single View (iOS) Application. Call it Calculator and make sure to check the Include Unit Tests checkbox, see Figure 1.

Create application

Figure 1: Create application

Click on the CalculatorTests directory once the Xcode editor opens. You should already be able to see that the shell of a test file, called CalculatorTests.swift has already been created, see Figure 2.

Test Shell

Figure 2: CalculatorTests.swift

At the top of the file we see

import XCTest
@testable import Calculator

Which imports the testing framework and uses the @testable swift command to tell the compiler that we want to test against the Calculator module.

The class is called CalculatorTests and it extends XCTestCase

class CalculatorTests: XCTestCase

There are 4 stub methods auto created, setUp() and tearDown() which run before and after every test, testExample(), which is a unit test stub and testPerformanceExample(), which we use when we want to know how long something takes to run. Note that all test methods in XCTest need to start with the word test. We get all this without having to write any code.

While we’re here let’s add an assertion to the testExample() method. Change the testExample() to following code in Listing 1 so we can see how the tests run in Xcode.

func testExample() {
   let result = 2 +2
   XCTAssert(result == 4, "something gone wrong here")
}
Listing 1: XCTAssert unit test

Listing 1 tests that 2+2 is indeed equal to 4 using XCTAssert. The string after the test is typically used to give you a hint on what test failed. But in this example it’s just a simple “something gone wrong here” catch all error message.

Click on the test tab, so we can see the Test Navigator. Run the test by right clicking on testExample() in the Test Navigator and choosing Test "testExample()". The green arrow tells us that it’s a passing test. You should see the same view as in Figure 3.

Test Navigator

Figure 3: testExample test passes

We can also see a report on how the tests ran if we right click again on testExample() in the Test Navigator and choose - Jump to report, see Figure 4.

Unit Test Report

Figure 4: Test report

While we don’t have that much to report yet, it does show us where we need to go when we start writing more comprehensive unit tests.

To complete our app we need to take the following steps
1. Create the User Interface or View
2. Create the Model code to perform the calculations
Once that’s completed we can return to units tests.

Creating the View

Figure 5 shows our calculator layout in Main.storyboard. The numbers and the operations are buttons and we’re using a text field to display the results.

Calculator Layout

Figure 5: Calculator Layout

To connect the numbers in our Main.storyboard. Open the Assistant Editor on Right so we can see the code. Click on each number at the same time as the control key and then drag it across to the code. Figure 6 shows that each button is an Action, with Name = compute, Type = UI Button and Event = Touch Up Inside. Repeat for each number.

Connect the numbers

Figure 6: Connect the numbers

Connect the operations in the same manner to the operation Action, see Figure 7. And do the same for do the same for the clear button which connects to the clear Action.

Connect the operations

Figure 7: Connect the operations

Finally in our view we need to connect the text field to display the results of our calculations which this time is an Outlet connection, we’re going to call it resultsFld and of type UITextField.

Connect text field

Figure 8: Connecting the clear button

Creating the Model Code

Listing 2 shows the complete code for our ViewController, including the compute, operation and clear fields. The operation code calls our CalculatorModel code which performs the calculations. We need to isolate the operations or Model code by putting it in a different class. This makes it possible to unit test our code.

	import UIKit

	class ViewController: UIViewController {
	    @IBOutlet var resultsFld: UITextField!

	    var res = Int()
	    var num = Int()
	    var op = String()   
	    let resCalc = CalculatorModel(a:0, b:0)    

	    override func viewDidLoad() {
	        super.viewDidLoad()
	        op = "="
	        resultsFld.text = ("\(res)")
	    }

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

	    @IBAction func compute(sender: UIButton) {
	        num = num * 10 + Int(sender.titleLabel!.text!)!
	        resultsFld.text = ("\(num)")
	    }

    @IBAction func operation(sender: UIButton) {

        switch op {
            case "=":
                res = num
            case "+":
                res = resCalc.add(res, b: num)
            case "-":
                res = resCalc.sub(res, b: num)
            case "*":
                res = resCalc.mul(res, b: num)
            case "/":
                res = resCalc.div(res, b: num)
            default:
                print("error")
        }

        num = 0
        resultsFld.text = ("\(res)")

        if(sender.titleLabel!.text == "=") {
            res = 0
        }

        op = sender.titleLabel!.text! as String!

    }

    @IBAction func clear(sender: UIButton) {
        res = 0
        num = 0
        op = "="
        resultsFld.text = ("\(res)")
    }
}
Listing 2: View Code

The code for our Calculator Model is shown in Listing 3. We have a simple init or constructor method and then very simple add, sub, mul and div functions.

	import Foundation

	class CalculatorModel {  
	    var a: Int
	    var b: Int

	    init(a:Int, b:Int){
	        self.a = a
	        self.b = b
	    }

	    func add(a:Int, b:Int) -> Int {
	        return a + b
	    }

	    func sub(a:Int, b:Int) -> Int {
	        return a - b
	    }

	    func mul(a:Int, b:Int) -> Int {
	        return a * b
	    }

	    func div(a:Int, b:Int) -> Int {
	        return a / b
	    }

	}
Listing 3: CalculatorModel.swift

Go back to CalculatorTests.swift in the CalculatorTests directory and add the testAdd() code in Listing 3 to do some simple unit tests on the add method in our CalculatorModel class.

func testAdd() {
	XCTAssertEqual(resCalc.add(1,b:1),2)
	XCTAssertEqual(resCalc.add(1,b:2),3)
	XCTAssertEqual(resCalc.add(5,b:4),9)
}
Listing 4: testAdd()

Figure 9 shows the test results for our unit test in the Test Navigator.

Connect text field

Figure 9: Test results.

We can do a lot more with our Swift unit tests which we’ll talk about in the next blog. But it is simplicity itself to set up and use unit testing with the XCTest library for Swift in Xcode

This is Part 2 of a series, the other blogs can be found below.

Part 1 - Swift Unit Testing on Ubuntu
Part 2 - Swift Unit Testing in Xcode
Part 3 - SonarQube, Jenkins and Swift
Part 4 - Swift GUI Testing with XCUI
Part 5 - Mocking in Swift with Cuckoo