Give your simulator superpowers

RocketSim: An Essential Developer Tool
as recommended by Apple

Using the #require macro for Swift Testing

Swift Testing is Apple’s modern replacement library for XCTest and introduces new macros like #require. We’ve covered the #expect macro before and are about to dive into its companion to set requirements for a given test.

Each #require macro needs to pass for a test to continue. It’s kind of an early guard not to continue unless all requirements are met. While you don’t need the macro to write tests using Swift Testing, it will make your tests clearer and easier to understand. Let’s dive in.

Setting test requirements using #require

Many tests need specific input requirements before they can start testing the target. You could write these requirements using regular assertions with the #expect macro. For example, when we have a Person struct with an optional initializer:

struct Person {
    let firstName: String
    let lastName: String

    var fullName: String {
        firstName + " " + lastName
    }

    init?(firstName: String, lastName: String) {
        guard !lastName.isEmpty else {
            return nil
        }
        self.firstName = firstName
        self.lastName = lastName
    }
}

We could write a requirement for testing the fullName to ensure our constructed person is not nil:

@Test func fullName() {
    let person = Person(firstName: "Antoine", lastName: "van der Lee")
    #expect(person != nil, "Person should be constructed successfully")
    #expect(person?.fullName == "Antoine van der Lee")
}

However, the #expect macro doesn’t make it readable and clear that it’s a requirement for the test even to get started. Self-explanatory code is robust and should always be your aim when working on a project. Therefore, we can rewrite the above test using the #require macro as follows:

@Test func fullName() throws {
    let person = Person(firstName: "Antoine", lastName: "van der Lee")
    try #require(person != nil, "Person should be constructed successfully")
    #expect(person?.fullName == "Antoine van der Lee")
}

It’s a slight nuance, but the test becomes easier to understand. We can further optimize this test by using the return value of the #require macro.

How do you stay current as a Swift developer?

Let me do the hard work and join 19,140 developers that stay up to date using my weekly newsletter:

Replacing XCTUnwrap

When using XCTest, we’ve learned that we can use XCTUnwrap to unwrap an optional or fail the test if it returns nil.

func testFullName() throws {
    let person = Person(firstName: "Antoine", lastName: "van der Lee")
    let unwrappedPerson = try XCTUnwrap(person)
    XCTAssertEqual(unwrappedPerson.fullName, "Antoine van der Lee")
}

In Swift Testing, we can create similar behavior by using the #require macro return value:

@Test func fullName() throws {
    let person = Person(firstName: "Antoine", lastName: "van der Lee")
    let unwrappedPerson = try #require(person, "Person should be constructed successfully")
    #expect(unwrappedPerson.fullName == "Antoine van der Lee")
}

The #require macro will fail the test immediately with a clear message if the value returns nil:

The #require macro fails a test early if a requirement isn't met.
The #require macro fails a test early if a requirement isn’t met.

Our test improved since we no longer have an optional within the #expect statement. This makes it clear that if our test fails at that line, it’s because the full name is wrong rather than the person being nil. These little improvements can significantly impact if you’re debugging tests and you don’t know what caused a failure.

Conclusion

The Swift Testing framework allows us to write modern tests with new APIs and macros like #require. While you don’t need the #require macro to write tests, it will make your code self-explanatory and more robust.

Here are a few more articles for you to make the most out of Swift Testing:

Thanks!

 
Antoine van der Lee

Written by

Antoine van der Lee

iOS Developer since 2010, former Staff iOS Engineer at WeTransfer and currently full-time Indie Developer & Founder at SwiftLee. Writing a new blog post every week related to Swift, iOS and Xcode. Regular speaker and workshop host.

Are you ready to

Turn your side projects into independence?

Learn my proven steps to transform your passion into profit.