Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Test-Driven iOS Development with Swift

You're reading from   Test-Driven iOS Development with Swift Write maintainable, flexible, and extensible code using the power of TDD with Swift 5.5

Arrow left icon
Product type Paperback
Published in Apr 2022
Publisher Packt
ISBN-13 9781803232485
Length 280 pages
Edition 4th Edition
Languages
Tools
Arrow right icon
Author (1):
Arrow left icon
Dr. Dominik Hauser Dr. Dominik Hauser
Author Profile Icon Dr. Dominik Hauser
Dr. Dominik Hauser
Arrow right icon
View More author details
Toc

Table of Contents (17) Chapters Close

Preface 1. Section 1 –The Basics of Test-Driven iOS Development
2. Chapter 1: Your First Unit Tests FREE CHAPTER 3. Chapter 2: Understanding Test-Driven Development 4. Chapter 3: Test-Driven Development in Xcode 5. Section 2 –The Data Model
6. Chapter 4: The App We Are Going to Build 7. Chapter 5: Building a Structure for ToDo Items 8. Chapter 6: Testing, Loading, and Saving Data 9. Section 3 –Views and View Controllers
10. Chapter 7: Building a Table View Controller for the To-Do Items 11. Chapter 8: Building a Simple Detail View 12. Chapter 9: Test-Driven Input View in SwiftUI 13. Section 4 –Networking and Navigation
14. Chapter 10: Testing Networking Code 15. Chapter 11: Easy Navigation with Coordinators 16. Other Books You May Enjoy

Assert functions in the XCTest framework

Each test needs to assert some expected behavior. The use of XCTAssert functions tells Xcode what is expected.

A test method without an XCTAssert function that doesn't throw an error will always pass.

The most important assert functions are listed here:

  • XCTAssertTrue(_:_:file:line:): This asserts that an expression is true.
  • XCTAssert(_:_:file:line:): This assertion is the same as XCTAssertTrue(_:_:file:line:).
  • XCTAssertFalse(_:_:file:line:): This asserts that an expression is false.
  • XCTAssertEqual(_:_:_:file:line:): This asserts that two expressions are equal.
  • XCTAssertEqual(_:_:accuracy:_:file:line:): This asserts that two expressions are the same, taking into account the accuracy defined in the accuracy parameter.
  • XCTAssertNotEqual(_:_:_:file:line:): This asserts that two expressions are not equal.
  • XCTAssertNotEqual(_:_:accuracy:_:file:line:): This asserts that two expressions are not the same, taking into account the accuracy defined in the accuracy parameter.
  • XCTAssertNil(_:_:file:line:): This asserts that an expression is nil.
  • XCTAssertNotNil(_:_:file:line:): This asserts that an expression is not nil.
  • XCTFail(_:file:line:): This always fails.

To take a look at a full list of the available XCTAssert functions, press Ctrl and click on the XCTAssertEqual word in the test that you have just written. Then, select Jump to Definition in the pop-up menu, as shown in the following screenshot:

Figure 1.5 – Jump to Definition of a selected function

Figure 1.5 – Jump to Definition of a selected function

Note that most XCTAssert functions can be replaced with XCTAssert(_:_:file:line). For example, the following assert functions are asserting the same thing:

// This assertion asserts the same as...
XCTAssertEqual(2, 1+1, "2 should be the same as 1+1")
// ...this assertion
XCTAssertTrue(2 == 1+1, "2 should be the same as 1+1")

But you should use more precise assertions whenever possible, as the log output of the more precise assertion methods tells you exactly what happened in case of a failure. For example, look at the log output of the following two assertions:

XCTAssertEqual(1, 2)
// Log output:
// XCTAssertEqual failed: ("1") is not equal to ("2")
XCTAssert(1 == 2)
// Log output:
// XCTAssertTrue failed

In the first case, you don't need to look at the test to understand what happened. The log tells you exactly what went wrong.

Custom assert functions

But sometimes, even the more precise assert function is not precise enough. In this case, you can write your own assert functions. As an example, let's assume we have a test that asserts that two dictionaries have the same content. If we used XCTAssertEqual to test that, the log output would look like this:

func test_dictsAreQual() {
  let dict1 = ["id": "2", "name": "foo"]
  let dict2 = ["id": "2", "name": "fo"]
  XCTAssertEqual(dict1, dict2)
  // Log output:
  // XCTAssertEqual failed: ("["name": "foo", "id":
    "2"]")...
  // ...is not equal to ("["name": "fo", "id": "2"]")
}

For the short dictionaries in this example, finding the difference is quite easy. But what if the dictionary has 20 entries or even more? When we add the following assert function to the test target, we get better log outputs:

func DDHAssertEqual<A: Equatable, B: Equatable>
  (_ first: [A:B],
   _ second: [A:B]) {
  if first == second {
    return
  }
  for key in first.keys {
    if first[key] != second[key] {
      let value1 = String(describing: first[key]!)
      let value2 = String(describing: second[key]!)
      let keyValue1 = "\"\(key)\": \(value1)"
      let keyValue2 = "\"\(key)\": \(value2)"
      let message = "\(keyValue1) is not equal to
        \(keyValue2)"
      XCTFail(message)
      return
    }
  }
}

This method compares the values for each key and fails if one of the values differs. Additionally, this assert function should check whether the dictionaries have the same keys. This functionality is left as an exercise for the reader. Here, we focus this example on how to write a custom assert function. By keeping the example short, the main point is easier to understand.

When we run this test with the preceding dictionaries, we see the following output in Xcode:

Figure 1.6 – Xcode showing the failure at two different places

Figure 1.6 – Xcode showing the failure at two different places

As you can see in the preceding screenshot, Xcode shows the test failure in the assert function. In the test method, it only shows a redirect to the failure. Fortunately, there is an easy fix for that. All we have to do is to pass file and line parameters to the custom assert function and use these in the XCTFail call, like this:

 func DDHAssertEqual<A: Equatable, B: Equatable>(
  _ first: [A:B],
  _ second: [A:B],
  file: StaticString = #filePath,        // << new
  line: UInt = #line) {                  // << new
    if first == second {
      return
    }
    for key in first.keys {
      if first[key] != second[key] {
        let value1 = String(describing: first[key]!)
        let value2 = String(describing: second[key]!)
        let keyValue1 = "\"\(key)\": \(value1)"
        let keyValue2 = "\"\(key)\": \(value2)"
        let message = "\(keyValue1) is not equal to
          \(keyValue2)"
        XCTFail(message, file: file, line: line)  // << new
        return
      }
    }
  }

Note that our assert function now has two new parameters: file and line, with the default values #filePath and #line, respectively. When the function is called in a test method, these default parameters make sure that the file path and the line of the call site are passed into that assert function. These parameters are then forwarded into the XCTAssert functions (XCTFail in our case, but this works with all XCT... functions). As a result, the failure is now shown in the line in which the DDHAssertEqual function is called, and we didn't have to change the call of the assert function. The following screenshot illustrates this:

Figure 1.7 – Improved failure reporting

This example shows how easy it is to write your own assert functions that behave like the ones that come with Xcode. Custom assert functions can improve the readability of the test code, but keep in mind that this is also code you have to maintain.

You have been reading a chapter from
Test-Driven iOS Development with Swift - Fourth Edition
Published in: Apr 2022
Publisher: Packt
ISBN-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.
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 $19.99/month. Cancel anytime
Banner background image