Unit Testing in Swift Tutorial

How To Get Started With iOS Unit Tests.

In computer programming, unit testing is a software testing method by which individual units of source code, sets of one or more computer program modules together with associated control data, usage procedures, and operating procedures, are tested to determine whether they are fit for use. Read more.

10 Reasons to Write Unit Tests

  1. Tests Reduce Bugs in New Features.
  2. Tests Reduce Bugs in Existing Features.
  3. Tests Are Good Documentation. A concise code example is better than many paragraphs of documentation.
  4. Tests Reduce the Cost of Change.
  5. Tests Improve Design.
  6. Tests Allow Refactoring.
  7. Tests Constrain Features.
  8. Tests Defend Against Other Programmers.
  9. Testing Forces You to Slow Down and Think.
  10. Tests Reduce Fear One of the biggest fears that programmers encounter is making a change to a piece of code and not knowing what is going to break.

1 Setting up your project

One of the biggest challenges to Unit Testing in Swift was the initial setup. Before Swift 2, you either had to make everything public or remember to add all your files to the testing target.

1.1 Starting with a new project

To get started, we are going to create a blank iOS project. You will want to have the include unit tests checkbox checked.

1.2 I already have a project, and I want to add tests

  1. Go to File > New > Target.
  2. Select iOS Unit Testing Bundle.
  3. Click Next.

2 Create an internal Class

Create a very simple internal Model Object in your project with a sample function. You don’t add Car to test target “UnitTestTests” and not making the class or yours methods public.

3 Apple’s Example Test Case

When your app is setup for testing, you will see a folder, UnitTestTests, with your tests in it. Xcode sets up a template, UnitTestTests.swift, test for you.

  1. Every test case you write will import XCTest framework.
  2. @testable import declaration is an extra import declaration. When you use @testable, there is no need to give any files in your application membership into your test target.
  3. To create a unit test, you will be subclassing a thing called an XCTestCase (class UnitTestTests: XCTestCase) .
  4. Try putting your mouse cursor over them. They are an inline button that runs your test. If you hover over the diamond next to it, you can click to run the test. Your app and its test target will get built, the test will run, and you will see the test succeeded notification.

This example test isn’t particularly useful. It doesn’t test anything. It just gives you a template to understand how to write tests. To see the real power of unit tests, we need to create something to test. 🤓

4 Setup and use Car class

Let’s start with a simple code to manage our cars:

//  Car.swift
import Foundation
class Car {
   var miles = 0
   var type: CarType
   var transmissionMode: CarTransmissionMode
   init(type:CarType, transmissionMode:CarTransmissionMode){
      self.type = type
      self.transmissionMode = transmissionMode
   }
   func start(minutes: Int) {
      var speed = 0
      if self.type == .Economy && self.transmissionMode == .Drive {
         speed = 35
}
      if self.type == .OffRoad && self.transmissionMode == .Drive {
        speed = 50
      }
      if self.type == .Sport && self.transmissionMode == .Drive {
        speed = 70
      }
      self.miles = speed * (minutes / 60)
}
}

enum CarType {
   case Economy
   case OffRoad
   case Sport
}
enum CarTransmissionMode {
   case Park
   case Reverse
   case Neutral
   case Drive
}

Now we can initialize and use a Car object “ferrari” in a ViewController:

//  ViewController.swift
import UIKit
class ViewController: UIViewController {
   override func viewDidLoad() {
      super.viewDidLoad()
      let ferrari = Car(type: .Sport, transmissionMode: .Drive)
      ferrari.start(minutes: 120)
      print(ferrari.miles) // => 140
   }
   override func didReceiveMemoryWarning() {
       super.didReceiveMemoryWarning()
   }
}

Ok it’s not terribly interesting, but will be enough for our first unit test.

5 Creating a Car Unit Test Case

To create new unit case in iOS:

  1. Go to File
  2. New File
  3. Then select Unit Test Case Class.
  4. In choose options, type class name: CarTests and press Next

When you click Next, you will be taken to another screen where you will decide the new CarTests file should go. Make sure you create it in your Unit Tests Group, and also make sure it is only a part of the unit test target, not the app target.

It’s another test case, but this time it’s for testing our Car Class.

//  CarTests.swift
import XCTest
class CarTests: XCTestCase {
   override func setUp() {
      super.setUp()
   }
   override func tearDown() {
      super.tearDown()
}
   func testExample() {
      // This is an example of a functional test case.
   }
   func testPerformanceExample() {
      // This is an example of a performance test case.
      self.measure {
         // Put the code you want to measure the time of here.
      }
   }
}

6 Customize CarTests

6.1 Use @testable to import your application

import XCTest
@testable import UnitTest

You do this before you declare your test case class, CarTests.

6.2 Declare the things you want to test

We will declare them as implicitly unwrapped optionals (!) because by the time our test case has set them up, we can guarantee they will be initialized and ready to test one another. Putting a ! in front of a variable in Swift means your code can expect the variable to always have a value when you use it.

class CarTests: XCTestCase {
   var ferrari:Car!
   var jeep:Car!
   var honda:Car!
}

6.3 Override the setup() method

To setup out Car in the test, we will override the setup() method. The Car will get setup with their corresponding types and transmission mode.

override func setUp() {
   super.setUp()
   ferrari = Car(type: .Sport, transmissionMode: .Drive)
   jeep = Car(type: .OffRoad, transmissionMode: .Drive)
   honda = Car(type: .Economy, transmissionMode: .Park)
}

6.4 Override the tearDown() method

We also want to make sure we clear everything out when each test finishes running. To that end, we need to override the tearDown() function on XCTestCase. Here we will simply set our Car to nil, removing any state they had.

override func tearDown() {
   super.tearDown()
   ferrari = nil
   jeep = nil
   honda = nil
}

7 Write a real test

Now that we’ve setup a test case to pit our Car against each other, we need to write a test that demonstrates how a sport car does more fast to a jeep car.

func testSportFasterThanJeep() {
   let minutes = 60
   //1 start ferrari
   ferrari.start(minutes: minutes)
   //2 start jeep
   jeep.start(minutes: minutes)
   //Test it
   XCTAssertTrue(ferrari.miles > jeep.miles)
}

Try it out by running this test (tapping on the diamond next to the test function you have created).

That last line of code you see is an assertion. It is a pass/fail sort of test. If the assertion passes, your test will pass. If the assertion fails, your test will fail. ✌️


Download source code

Check out the example source code on Github.


A good book to learn more

Test-Driven iOS Development With Swift

amazon.com

If you enjoyed this article please recommend and share.
This is the post you are looking for.
Like what you read? Give Enrico Piovesan a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.