Reader small image

You're reading from  Test-Driven iOS Development with Swift - Fourth Edition

Product typeBook
Published inApr 2022
PublisherPackt
ISBN-139781803232485
Edition4th Edition
Right arrow
Author (1)
Dr. Dominik Hauser
Dr. Dominik Hauser
author image
Dr. Dominik Hauser

Dr. Dominik Hauser is an iOS developer working for a small company in western Germany. In over 11 years as an iOS developer, he has worked on many different apps, both large and small. In his spare time, Dominik builds small (often silly) apps and explores how he can become a better iOS developer. He talks at conferences, writes books, and is active in the iOS community in general. His most successful open source component (in terms of stars) is a pull-to-refresh control for a table view that consists of an actual SpriteKit game. Before Dominik became an iOS developer, he was a physicist, researching the most powerful photon sources within our galaxy.
Read more about Dr. Dominik Hauser

Right arrow

Chapter 3: Test-Driven Development in Xcode

For test-driven development (TDD), we need a way to write and execute unit tests. We could write the tests into the main target of our Xcode project but that would be impractical. We would have to separate the test code from the production code somehow, and we would have to write some scripts that execute the text code and gather feedback about the results of the tests.

Fortunately, this has already been done. It all started in 1998, when the Swiss company Sen:te developed OCUnit, a testing framework for Objective-C (hence the OC prefix). OCUnit was a port of SUnit, a testing framework that Kent Beck had written for Smalltalk in 1994.

With Xcode 2.1, Apple added OCUnit to Xcode. One reason for this step was that they used it to develop Core Data at the same time that they developed Tiger, the OS with which Core Data was shipped. Bill Bumgarner, an Apple engineer, wrote this later in a blog post:

"Core Data 1.0 is not perfect...

Technical requirements

An example of TDD

For this TDD example, we are going to use the same project we created in Chapter 1, Your First Unit Tests. Open the FirstDemo project in Xcode, and run the tests by hitting U. The test we wrote to explore the custom assert function is failing. We don't need this test anymore. Delete it.

Let's say we are building an app for a blogging platform. When writing a new post, the user puts in a headline for the post. All the words in the headline should start with an uppercase letter.

To start the TDD workflow, we need a failing test. The following questions need to be considered when writing the test:

  • Precondition: What is the state of the system before we invoke the method?
  • Invocation: How should the signature of the method look? What are the input parameters (if any) of the method?
  • Assertion: What is the expected result of the method invocation?

For our blogging app example, here are some possible answers to these questions...

Finding information about tests in Xcode

With Xcode 5 and the introduction of XCTest, unit testing became tightly integrated into Xcode. Apple added many UI elements to navigate to tests, run specific tests, and find information about failing tests. Over the years, they improved the integration further. One key element here is the test navigator.

Test navigator

To open the test navigator, click the diamond with the minus sign in the navigator panel or use the shortcut 6:

Figure 3.1 – The test navigator in Xcode

The test navigator shows all tests in the open project or workspace. In the preceding screenshot, you can see the test navigator for our demo project. In the project, there are two test targets, one for the unit tests and one for the UI tests. For complex apps, it can be useful to have more than one unit tests target, but this is beyond the scope of this book. The number of tests is shown right behind the name of the test target...

Running tests

Xcode provides many different ways to execute tests. You have already seen two ways to execute all the tests in the test suite: go to the Project | Test menu item or use the U keyboard shortcut.

Running one specific test

In TDD, you normally want to run all the tests as often as possible. Running the tests gives you confidence that the code does what you intended when you wrote the tests. In addition to this, you want immediate feedback (that is, a failing test) whenever new code breaks a seemingly unrelated feature. Immediate feedback means that your memory of the changes that broke the feature is fresh, and the fix is made quickly.

Nevertheless, sometimes, you need to run one specific test, but don't let it become a habit. To run one specific test, you can click on the diamond visible next to the test method:

Figure 3.6 – Run one specific test by clicking the diamond next to the test method in the gutter

When you...

Setting up and tearing down

We have already seen the setUpWithError() and tearDownWithError() instance methods earlier in this chapter. The code in the setUpWithError() instance method is run before each test invocation. In our example, we used setUpWithError() to initialize the Blogger that we wanted to test. As it was run before each test invocation, each test used its own instance of Blogger. The changes we made to this particular instance in one test didn't affect the other test. The tests are executed independently of each other.

The tearDownWithError() instance method is run after each test invocation. Use tearDownWithError() to perform the necessary cleanup. In the example, we set the blogger to nil in the tearDownWithError() method.

In addition to the instance methods, there are also the setUp() and tearDown() class methods. These are run before and after all the tests of a test case, respectively.

Debugging tests

Sometimes, but not often, you may need to debug your tests. As with normal code, you can set breakpoints in test code. The debugger then stops the execution of the code at that breakpoint. You can also set breakpoints in the code that will be tested to check whether you have missed something or whether the code you'd like to test is actually executed.

To get a feeling of how this works, let's add an error to a test in the preceding example and debug it:

  1. Open FirstDemoTests.swift and replace the test method test_makeHeadline_shouldCapitalisePassedInString_2() with this code:
    // FirstDemoTests.swift
    func 
     test_makeHeadline_shouldCapitalisePassedInString_2()
     { 
      let input = "The contextual action menu"  
      let result = blogger.makeHeadline(from: input)
      let expected = "The ContextuaI Action Menu"
      XCTAssertEqual(result, expected)
    }

Have you seen the error that we have...

Summary

In this chapter, we explored how unit testing and TDD works in Xcode. We saw real tests testing real code. Using the different test-related features of Xcode, we learned to write, run, and fix tests and to find test-relevant information. All this is important for the rest of the book. We need to know how to use Xcode when doing TDD.

In the next chapter, we will figure out the app we are going to build using test-driven development.

Exercises

  1. Write a test for a method that reverses a string. Write the code that makes the test pass.
  2. Write a test for a method that takes a headline and creates a filename from it. In the filename, make sure that spaces are replaced by _ and it only contains lowercase characters.
lock icon
The rest of the chapter is locked
You have been reading a chapter from
Test-Driven iOS Development with Swift - Fourth Edition
Published in: Apr 2022Publisher: PacktISBN-13: 9781803232485
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
undefined
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at €14.99/month. Cancel anytime

Author (1)

author image
Dr. Dominik Hauser

Dr. Dominik Hauser is an iOS developer working for a small company in western Germany. In over 11 years as an iOS developer, he has worked on many different apps, both large and small. In his spare time, Dominik builds small (often silly) apps and explores how he can become a better iOS developer. He talks at conferences, writes books, and is active in the iOS community in general. His most successful open source component (in terms of stars) is a pull-to-refresh control for a table view that consists of an actual SpriteKit game. Before Dominik became an iOS developer, he was a physicist, researching the most powerful photon sources within our galaxy.
Read more about Dr. Dominik Hauser