Chapters

Hide chapters

iOS Test-Driven Development by Tutorials

Second Edition · iOS 15 · Swift 5.5 · Xcode 13

4. Test Expressions
Written by Michael Katz

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

The TDD process is straightforward, but writing good tests may not always be. Fortunately, each year, Xcode and Swift have become more capable. This means you have many features at your disposal that help with both writing and running tests.

This chapter covers how to use the XCTAssert functions. These are the primary actors of the test infrastructure. You’ll go through gathering code coverage to verify the minimum amount of testing. Finally, you’ll use the test debugger to find and fix test errors.

In this chapter, you’ll learn about:

  • XCTAssert functions
  • UIViewController testing
  • Code Coverage
  • Test debugging

Note: Be sure to use the Chapter 4 starter project rather than continuing with the Chapter 3 final project. It has a few new things added to it, including placeholders for the code to add in this tutorial.

Assert methods

In Chapter 3, “TDD App Setup,” you used XCTAssertEqual exclusively. There are several other assert functions in XCTest:

  • Equality: XCTAssertEqual, XCTAssertNotEqual
  • Truthiness: XCTAssertTrue, XCTAssertFalse
  • Nullability: XCTAssertNil, XCTAssertNotNil
  • Comparison: XCTAssertLessThan, XCTAssertGreaterThan, XCTAssertLessThanOrEqual, XCTAssertGreaterThanOrEqual
  • Erroring: XCTAssertThrowsError, XCTAssertNoThrow

Ultimately, any test case can be boiled down to a conditional: (does it meet an expectation or not) so any test assert can be re-composed into a XCTAssertTrue.

Note: With XCTest, a test is marked as passed as long as there are no failures. This means that it does not require a positive XCTAssert assertion. A test with no asserts will be marked as success, even though it does not test anything!

App state

In the previous chapter, you built out the functionality to move the app from a not started state to an in-progress one. Now is a good time to think about about the whole app lifecycle.

Yaaqbh Ravnqekut Gov Fmuwtud En Cludpotq Caimid zqoqm() caobu() gayobe() vincujw() Tnext

Asserting true and false

To build out the state transitions, you need to add some more information to the app about the user. The completed and caught states depend on the user activity, the set goal and Nessie’s activity. To keep the architecture clean, the app state information will be kept separate from the raw data that is tracking the user.

Testing Errors

If the optional goal property isn’t set, it doesn’t make sense for the app to enter the inProgress state. Therefore starting the app without a goal is an error!

View controller testing

Now that the model can have a goal set and an app state that checks it, the next feature is to expose that the state to the user. In the previous chapter, you wrote some unit tests for StepCountController. Now build on that with some proper view controller unit testing.

Functional view controller testing

The important thing when testing view controllers is to not test the views and controls directly. This is better done using UI automation tests. Here, the goal is to check the logic and state of the view controller.

Using the host app

The next requirement for the app is that the central view should show the user’s avatar in the running position. The word should signifies an assertion, so you’ll write one, now. First, open StepCountControllerTests.swift. Next, add the following under // MARK: - Chase View:

Fixing the tests

Go back to StepCountControllerTests.swift, and replace setUpWithError() with the following:

Test ordering matters

Build and test the whole target, and most of the tests should pass, but not testController_whenCreated_buttonLabelIsStart. This test fails.

Randomized order

There is also an option in the Test action of the scheme to randomize the test order. Edit the FitNess scheme. Select the Test action. In the center pane, next to FitNessTests is an Options… button. Click that and, in the pop-up, check Randomize execution order. This will cause the tests to run in a random order each time.

Code coverage

While on the subject of the scheme editor, open up the Test Action again. This time select the Options tab. There is a checkbox for Code Coverage. Check it.

Debugging tests

When it comes to debugging tests, you’ve already practiced the first line of defense. That is: “Am I testing the right thing?”

Using test breakpoints

With Nessie in the picture, the data model gets a little more complicated. Here are the new rules with Nessie:

Completing coverage

If you take a look at the code coverage for DataModel.swift, it is no longer 100%. If you look at the file, notice the striped annotation in the updated caught. Hovering over the stripe shows that not of all the conditions were checked. The 0 tells you there is more test.

Finishing out the requirements

There is one final piece that hasn’t been accounted for yet: The user cannot reach the goal if they have been caught. Add this test to the Goal tests section:

Challenge

In StepCountControllerTests.tearDownWithError(), there are separate calls to reset the AppModel and the DataModel. Since the data model is a property of the app model, refactor the data model reset into AppModel.restart(), along with the appropriate tests.

Key points

  • Test methods require calling a XCTAssert function.
  • View controller logic can be separated in to data/state functions, which can be unit tested and view setup and response functions, which should be tested by UI automation.
  • Test execution order matters.
  • The code coverage reports can be used to make sure all branches have a minimum level of testing.
  • Test failure breakpoints are a tool on top of regular debugging tools for fixing tests.

Where to go from here?

For more on code coverage, this video tutorial covers that topic. And you can learn everything and more about debugging from the Advanced Apple Debugging & Reverse Engineering book. The tools and techniques taught in that tome are just as applicable to test code as application code.

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2026 Kodeco Inc.

You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now