Getting Started with Automated Testing in Xcode

Xcode comes with some powerful testing and Continuous Integration functionality built-in. This article will give you a basic introduction to using these tools and show you how they can help write maintainable, testable code.

We will walk through the process of setting up a spare computer to do continuous integration. This computer will monitor a repository, run tests, and then produce a report when the repository has changes made to it.

Having a spare computer do continuous integrations means you don’t have to do this manually. Developing apps involves writing code and then pushing it to a repository. After that, if you want anyone to see/use it, you have to distribute the code. You can automate the process that runs tests (to confirm the code that you’ve committed does what you want it to do and doesn’t break anything in the process) then make it available to those who need it (clients, managers, testers/QA, etc.) While testing is a simple way to catch regressions before they make it into a user’s hands, writing and running tests will help you write modular, testable code.

Walkthrough

General set-up

To follow this tutorial, you will need:

  • your usual development Mac
  • another Mac running OS X Yosemite
  • a domain name
  • the latest Xcode installed (6.4 as of publication)
  • our sample project: Degrees
  • patience

OS X Server set-up

Download and install OS X Server on the second Mac (the remote machine). It sounds like we’ll be reinstalling OS X, but really it’s just an app. We will be using OS X Server 4.1. It costs CAD$22.99 on the Mac App Store unless you qualify for the Up-to-Date program or if you are a registered Mac or iOS developer.

server-install-480
Figure 1 – Installing OS X Server

Once it’s installed and you’ve ensured it works, get the domain set up and pointing to a directory. (It’s probably best to put the website files in ~Sites/yourDomainName)

  1. In OS X Server, select Services > Websites
  2. Turn it on
  3. Click the + button at the bottom
  4. Enter the domain name. (Optional) Change Store Site Files In to point to the folder where you will put your website’s files.
    websites-2-480 Figure 2 – Domain configuration
  5. Click Create
  6. Check that the domain is running. This is where we will see the results of each integration.

Xcode Server set up

Xcode Server is actually just regular old Xcode. You will need to have it installed in order to complete the remaining steps. Once it’s installed, select Xcode from the services list on the left then click Choose from the Builds section. This step will take a while.

Next we will choose the version of Xcode to work with. If you have several versions installed, this is the place to specify which one you will use to run the automated builds.

choose-xcode-480

fig. 3 – Choosing an Xcode version

Now run it by clicking the toggle switch to turn Xcode Server on.

xcode-off-480

fig. 4 – Xcode is off

This step will also probably take a while. Be patient.

configuring-xcode-480

fig. 5 – Configuring Xcode Server

Build results will show in your browser at http://127.0.0.1/xcode. You should see the following screen telling you that there are no bots configured and no local projects:

no-bots-configured-480

fig. 6 – No bots configured

Bots

So what is a bot? It’s a process that is responsible for carrying out a task on your project. A bot can be responsible for simply building the project or it could be configured to do more advanced tasks like analyzing performance, running a test suite, and/or archiving your app. This is where we will set up a bot to build and test our sample application.

Before we set up a bot, we need to create a scheme. A scheme is a set of guidelines that tell the bot what to do and when (eg. “build and test my app any time I check code into the master branch”). This is specified in the Xcode project.

Project setup

We will need a project to work with. We are going to use the Degrees sample project to test our Xcode setup. Clone the sample project to your local development machine. It should build without warnings or errors. There are two tests that we will run after we have our bot up and running. You will need to change the remote location to a repository where you have write access. THIS IS VERY IMPORTANT!  It is where you will be committing your code from your development machine. It is also the repository that Xcode Server will monitor for changes to start a build. You can put this on a schedule if you prefer; more on that later.

In Xcode, follow these steps to share the project with your remote bot: click Product > Schemes > Manage Schemes

manage-schemes-480

fig. 7 – Manage schemes

Check the Shared box and close the schemes pane.

shared-480

fig. 8 – Shared scheme

Commit this change to your repo and push it. (Pro tip: Make sure that this file isn’t ignored in your .gitignore).

This is the fun part where we configure our bot to do stuff!

Log in to your remote Mac. This is where we’ll be setting up Xcode’s first bot. Clone the project, then click Product > Create Bot. Enter a name for it, and add your server’s address (the IP address of the remote machine that will run your bots). Leave the Integrate Immediately box checked so that the first integration will run as soon as the set-up process is finished. You will need to provide your repo credentials here.

After providing your credentials we can set up a schedule. We’re going to select On Commit for the bot’s schedule and select only Perform test action for it’s actions. Checking the Perform analyze action box will make Xcode run static analysis on your project. This is useful for catching memory leaks and other fun surprises.

integration-schedule-480

fig. 9 – Selecting an integration schedule

We can choose which simulators will run our tests. This is great for functional testing to make sure the UI is consistent across display sizes. For now, we’ll just select the iPhone 6 simulator.

test-device-480

fig. 10 – Select a simulator

Next we can specify triggers for before and after integration. For example, in the project my team is working on at Kunai we have a script that updates the build number in Xcode before each run. Choose whether you want to be notified of build errors, failures, etc. then click Update. Our bot is configured and waiting to be triggered by a commit to the repository.

Our first integration

Since we left the Integrate Immediately button checked, Xcode will run an integration as soon as we finish our set up. The sample project has some tests included so those will be run during each integration. We can now check in on the bot to see how it went:

integration-failed-480

fig. 11 – Integration failed

Clearly, we’ve got a problem here. We see that the application built without errors, warnings, or issues but one of the tests failed.

test-fail-480

fig. 12 – Test failed

Specifically, it looks like there’s something wrong with the conversion from Fahrenheit to Celsius. Let’s fix that by changing the 50.0 to 5.0 in fahrenheitToCelsius(degreesF).

calculation-fixed-480

fig. 13 – Calculation fixed

We could run the tests locally or we can let our fancy new integration take care of it. Normally we would test and analyze our code before committing, but this is a trivial (and contrived!) example, so we’re going to let our bot handle it this time. Commit and push the code. We should see the test pass.

Xcode polls the repository periodically, so you may not see the bot start right away. Give it a few minutes. Once the next integration is finished we can check the results: no errors, warnings, or issues and both tests passed.

integration-passed-480

fig. 14 – No failing tests

test-pass-480

fig. 15 – Tests pass

Let’s check this out in the browser to see what it looks like. If you’re not logged in, you should see this:

integration-results-480

fig. 16 – Integration Results

Now that we’ve got the results we want, this integration cycle is complete. In the future, it will be easy to monitor integration results (by clicking here) and we can be assured that we have one more tool to help catch regression bugs.

Conclusion

Here’s what we’ve done:

  • set up OS X Server
  • set up a domain
  • set up an Xcode service
  • added a sample project with unit tests
  • configured Xcode to run tests and build when it notices a commit to our repository
  • fixed a failing test
  • reviewed automatic build status

Through those activities, we learned why CI and testing is important as well as how easy it can be to get started testing applications by using Xcode’s built-in tools.

If you’re looking to expand on what we’ve covered so far, here are a few ideas to extend the automated testing setup we’ve created:

  • Validate the input. The user can input anything into the text field. Make sure that only numbers have been entered before converting.
  • Validate the output. The converted temperature is displayed with several digits after the decimal places even though we don’t need more than one. Change the output to show only one digit after the decimal.
  • Move the conversion functions out of the view controller. This project doesn’t stick to the MVC pattern. Change that by creating a model for the units and having the controller talk to it then update the view accordingly. This will also make testing easier in the future as the model and the controller aren’t mixed together in one file or class.
  • Add email notifications for build results.

We’ll pick up from here and discuss Xcode 7’s new features in the next installment.


Sources

Xcode CI guide

Pro iOS Continuous Integration