Swift Unit Testing

A guide from theory to practice

Guilherme Paciulli
Apple Developer Academy | Mackenzie
7 min readMar 12, 2018

--

Unit Tests

Unit tests are a development testing technique designed to work faster, easier and more effectively than the ordinary way of opening your application and imitating user behavior to check if a functionality is working properly. The name Unit Test is not in vain: it literally tests the simplest unit of your application by running methods and checking variables behavior on certain conditions.

Usually an unit test is a class that tests another class using a framework that simulates key things of the language and the development environment you are programming on so that it can run the code of that class separately. Please note that certain resources, for example database connections and changes, although possible to be simulated in an unit test, are normally not implemented by default, so they cannot be used initially.

There are a few characteristics to build a good unit test, a common rule is the FIRST acronym:

  • Fast — it gotta run fast, otherwise it would completely miss a valuable characteristic of a unit test against an ordinary test.
  • Independent — it shouldn’t depend of the results of another test or involve other unit — a unit test is meant to test only one unit.
  • Repeatable — it gotta return the same result every time it runs, once you say that a certain class with certain inputs should return certain outputs: it will need to respect these rules forever.
  • Self-Validating — it gotta be totally automated and with clear results (PASS or FAIL).
  • Timely — it's gotta be planned.

Upon this testing technique, a development tactic was created: TDD, or Test Driven Development. It is a software development methodology that consists in planning and creating the tests for each software unit before developing it. Usually, that methodology uses a unit test framework.

Swift and Unit Tests

The unit tests run in a separated target. You can have more than one test target, for different tests. For example, you could have one target to run tests to asynchronous code (which takes more time), and another one to synchronous code (which is faster). This way, you could run them separately. Because we are working with an object oriented language, the unit tests are gonna test our classes. So, for each class of our model, we’ll have a unit test class.

Tutorial

We are going to build a very basic project to teach how to implement a unit test in XCode using Swift 4 as the programming language. The project will not have any graphical contents: just us, the code and a cup of your favorite Arabic coffee.

The project from where we will start is in Github, clone it to your computer so we can continue, find it here.

The repository does not have XCode’s unit test support, so we will teach you how to add it. Go to your test navigator (cmd+6), click on the little plus on the bottom and select New Unit Test Target, then click finish:

These are the ways to run our unit test project:

  • Press “command + u” or, in XCode, go to Product > Run — it will run every tests in every classes of all targets
  • Click in the diamond in the side of the name of the unit test class or in the side of the test function name — it will run all unit tests of a class, or a specific unit test of a function.
  • In the list of unit tests click the play button at the side of the target, the class or the function:

Now let's see the test classes, these is its default structure:

  • 1 — This import allows us to create a swift unit test class.
  • 2 — This notation is used to allow us to have access to our model classes inside our test container. Without that import you’ll not be able to see the classes you created out of the test target. When the project is created with unit tests, this notation comes by default.
  • 3 — This defines our class as a unit test class. Every unit test class has to extends this class.
  • 4 — This function is used to initialize the data we need to make our tests. It is called every time before a test function.
  • 5 — This function is used to clean the data we used in our test. It is called every time after a test function.
  • 6 — All test functions' names must start with the word “test”

Make this class like this:

  • 1 — These are our lab rats — our tests subjects.
  • 2 — On the setUp() we initiallize our test subjects
  • 3 — On tearDown() we set their value to nil

Basically our test will guarantee that an axe deals more damage than a sword… Ok, let me explain that a little bit more: in our RPG system an axe deals more damage than a sword:

Just check the file Weapon.swift to make sure of that:

… An axe deals 60 points of damage and a sword deals only 50, but how to make sure? Let's test that with a method, go back to the RPGTests class that you created and create this method:

Following the lines of code, we can understand what's happening here: We are testing if an axe is stronger than a sword, given a hero — instance of a warrior with an axe — and a enemy — another instance of a warrior, now with a sword. Now let's make them fight: the hero attacks the enemy and the enemy attacks the hero. Since an axe is stronger than a sword and the hero has an axe, we can assert that the hero's health will be greater than enemy's health. That's our assertion. If that's false, the test has failed, otherwise it passed!

Run the tests, click the little diamond located at the same line as the test function. See? It's working like a charm… Or at least I hope so.

Now let’s test something a bit more complex. Based on our model, some hero classes are better with specific types of weaponry, which grants them a 20 damage buff. For example, the mage is very skilled in the magic arts, so instead of dealing 40 damage by attacking with a staff, the mage will deal 60 damage. This is better explained in the image below:

So according to our model, a hunter with an axe will deal 60 damage, since he doesn’t receive the damage buff. A mage with a staff will also deal 60 damage, because of the damage buff. So, lets test if this mechanic is working:

Run the tests and …oopsie-daisies…

The test failed, and the error report we got tells us that the values are not equals when we know they were supposed to be. That means something in our model is not working according to what we planned. Go to the Mage.swift class and uncomment the following code:

Now, we have applied the damage buff to the mage class as well. Go back to the test class and run the test once again and then…

:)

Now that you already know how a unit test works, and how to make it using swift and XCode, you can check the apple official documentation to see a more detailed list of Assert functions and test cases here.

Optionally, to see the list of available assert functions you can command click on any Assert function and select “jump to definition” to see them, like in the image bellow:

That’s it for the basics of unit testing. As training, you could try testing out all possibilities of weapons and character classes, comparing the test results with the expected results. See you on the next article!

Github / This medium ;)
Github / Medium
Github / Medium

--

--