Swift Unit Testing on Ubuntu

Swift was announced at the WWDC in 2014 and late last year the code was open sourced. It can run on both OSX and Ubuntu which is a huge departure for Apple which has typically been a more closed system. The language Swift is a completely different animal to Objective-C.

Being brand new it doesn’t have many of the arcane aspects of the much older Objective-C. Its learning curve is much shallower than Objective-C and will allow new developers to quickly crank out iOS apps. Well that’s Apple’s plan anyway.

However Swift is evolving quickly and already has built in unit testing using XCTest. In this blog we’re going to look at how to do a simple unit test written in Swift, on the Ubuntu Linux platform without XCode. And I’m also going to contradict what I said earlier about Swift, as units testing is the one area where Swift isn’t very smooth or obvious. So it makes it a good candidate for a blog until it gets fixed in Swift 3.x or 4.x.

Hello, World Unit Test

Before we go any further let’s look at a simple unit test. For demonstration purposes we can use the Add method from a Calculator example, which is available here, see Listing 1.

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

Listing 1: Calculator Add method

A very simple unit test is shown in Listing 2 which tests if the Add method can add two numbers correctly.

func testAddCheck() {
    XCTAssertEqual(calc.add(1,1),2)
}

Listing 2: One plus one equals two test method

Unit tests use assertions to make sure the method provides an expected result. In this case we’re using XCTAssertEqual to see if the Add method returns 2 when adding 1 + 1. If the test works then we should get the following when we run the swift test command, see Listing 3.

$ swift test   
Compiling Swift Module 'CalculatorTestSuite' (1 sources)        
Linking .build/debug/test-Package    
Test Case 'CalculatorTests.testAddCheck' started.     
Test Case 'CalculatorTests.testAddCheck' passed (0.0 seconds).    
Executed 1 test, with 0 failures (0 unexpected) in 0.0 (0.001) seconds   
Total executed 1 test, with 0 failures (0 unexpected) in 0.0 (0.001) seconds Listing 3: Test output

Benefits

If you’re new to Agile development you’re probably wondering how Agile can improve the development process. At its most basic Agile and unit testing in particular helps us to …

  • Catch more mistakes
  • Confidently make more changes
  • Built in regression testing
  • Extend the life of your codebase

If you write unit tests and they cover a significant portion of your code then you’re going to catch more bugs. You can make simple changes to tidy up the code or make extensive architectural changes. But if you run your unit tests and they all pass then you can be confident that you didn’t introduce any subtle defects.

The more unit tests you write the more you can regression test your app whenever you change the code. And once you have a lot of unit tests then it becomes a regression test suite that allows you to have the confidence to do things you wouldn’t otherwise attempt.

Unit tests mean you no longer have to program with a ‘leave well enough alone’ mindset. You can now make significant changes, such as changing to a new database, updating your backend API, even changing from Objective-C to Swift and be happy that your app is behaving the same as before you made the changes if all the tests execute without any errors.

Agile Test Pyramid

There are several types of tests you need in your test suite to make sure your app is fully tested. You should have Unit Tests for the component or method level functionality, API tests for any backend RESTful APIs and GUI tests for Android activities and general application workflow.

The classic Agile Test Pyramid which first appeared in Mike Cohn’s book Succeeding with Agile is shown in Figure 1. This is a good guide for the relative quantity of each types of tests your app is going to need.

Agile Pyramid

Figure 1: Agile Pyramid

Create Hello World Unit Test in Swift

In the following example we show how to create our simple unit test example using Ubuntu Swift. This should return true assuming adding two numbers in the calculator app works correctly.

To set up and run a unit test you need to perform the following tasks

  • Prerequisites
  • Create and compile Calculator code
  • Create Test code
  • Add Ubuntu tweaks
  • Run unit tests

Prerequisites

You need to be using either Ubuntu 14.04 or 15.10. Download the latest 3.x development snapshot from here This is the compiled Swift environment so you can unzip it and add it to your path as follows, modifying your snapshot name appropriately.

$ tar -xvzf swift-DEVELOPMENT-SNAPSHOT-2016-02-25-a-ubuntu14.04.tar.gz
$ pwd
$ export PATH=/home/ubuntu/swift-DEVELOPMENT-SNAPSHOT-2016-02-25-a-ubuntu14.04/usr/bin:$PATH

Listing 4: Creating a Swift environment

Note: Clint Cabanero has a great guide on how to install Swift from scratch on an Amazon EC2 available here

Create and Compile Code

Create a Sources folder and add the code in Listing 5 to Calculator.swift

class Calculator {  
    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
    }
}

Listing 5: Calculator.swift

Before we compile the code we need to create a Package Manager file, see Package.swift in Listing 6.

import PackageDescription

let package = Package(
    name: "Calculator"
)

Listing 6: Package.swift

If everything is set up correctly you should have the following structure, see figure 2.

Original Directory Structure
Figure 2: Initial Directory Structure

Run the swift build command which will create a .build directory if the code compiles.

Create Test Code

Create a Tests directory and enter the code in Listing 7 into a CalculatorTest.swift file in Tests/Calculator.

import XCTest
@testable import Calculator

class CalculatorTests: XCTestCase {
  var calc : Calculator!

  override func setUp() {
    super.setUp()
    calc = Calculator()
  }

  func testAddCheck() {
    XCTAssertEqual(calc.add(1,1),2)
  }
}

Listing 7: CalculatorTests.swift

CalculatorTests imports the Calculator code and the XCTest framework. We have a setUp method that initializes our calc object and finally we have a test method to check that 1 + 1 does indeed equal 2.

Ubuntu Tweaks

In XCode the code in Listing 7 would work fine, but we need a few extras tweaks in Ubuntu. First create a LinuxMain.swift file in the Tests directory which creates the constructor for the tests, see Listing 8.

import XCTest
@testable import CalculatorTestSuite

XCTMain([
    testCase(CalculatorTests.allTests)
])

Listing 8: LinuxMain.swift

Finally we also to create an XCTest extension to your test case in CalculatorTests.swift, see Listing 9 which lists all our tests methods.

extension CalculatorTests {
    static var allTests : [(String, CalculatorTests -> () throws -> Void)] {
        return [
            ("testAddCheck", testAddCheck)
        ]
    }
}

Listing 9: XCTest extension

Figure 3 shows the directory structure for our code and our tests.
Test Directory Structure

Figure 3: Project directory structure

Run Tests

Run the command swift test to see the result of our unit tests. If your tests are successful they show the output again shown in Listing 10.

Executed 1 test, with 0 failures (0 unexpected) in 0.0 (0.0) seconds
Total executed 1 test, with 0 failures (0 unexpected) in 0.0 (0.001) seconds

Listing 10: Test results

Summary

In this post we looked at the current state of Swift unit testing on the Ubuntu platform. In the next blog we’ll explore Agile testing in a lot more detail so you can see how to apply these techniques to your application to produce cleaner, faster code with less defects.

This is Part 1 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