NEW: Join my free 100 Days of SwiftUI challenge today! >>

How to test asynchronous functions using expectation()

Swift version: 5.1

Paul Hudson    @twostraws   

XCTestCase has the built-in ability to work with asynchronous code using a system of expectations. First, you create one of more instances of XCTestExpectation using the expectation() method, then you run your asynchronous code, and finally you call waitForExpectations() so the test doesn’t end prematurely.

When your asynchronous code completes you call fulfill() on it to mark it as complete, and you can then call some variant of XCTAssert() to check whether the test succeeded or failed.

As an example, I have a FeedParser struct that loads stories from disk and parses them ready for display. It takes a few milliseconds to run, so to avoid freezing my app it has an asynchronous method called loadStories() that calls a completion handler when the stories are ready to be used. Using XCTestCase expectations I would write a test like this:

func testStoryLoading() throws {
    let parser = FeedParser()

    // create the expectation
    let exp = expectation(description: "Loading stories")

    // call my asynchronous method
    parser.loadStories {
        // when it finishes, mark my expectation as being fulfilled
        exp.fulfill()
    }

    // wait three seconds for all outstanding expectations to be fulfilled
    waitForExpectations(timeout: 3)

    // our expectation has been fulfilled, so we can check the result is correct
    XCTAssertEqual(parser.stories.count, 20, "We should have loaded exactly 20 stories.")
}

If my asynchronous code does not complete in the allotted time of three seconds, the test is considered an immediate failure.

XCTestExpectation has two properties you might want to explore further. The first is isInverted: if you set that to true then the test will be considered a failure if fulfill() gets called before the time out, so for example you might want the AI in your game to wait at least two seconds before making its move so the player can see it’s definitely thinking.

The second is expectedFulfillmentCount: if you set this to 5 for example, it means fulfill() must be called five times before the time out is reached, which allows you to implement more advanced testing logic.

LEARN SWIFTUI FOR FREE I have a massive, free SwiftUI video collection on YouTube teaching you how to build complete apps with SwiftUI – check it out!

Available from iOS

Similar solutions…

About the Swift Knowledge Base

This is part of the Swift Knowledge Base, a free, searchable collection of solutions for common iOS questions.

BUY OUR BOOKS
Buy Pro Swift Buy Swift Design Patterns Buy Testing Swift Buy Hacking with iOS Buy Swift Coding Challenges Buy Swift on Sundays Volume One Buy Server-Side Swift (Vapor Edition) Buy Advanced iOS Volume One Buy Advanced iOS Volume Two Buy Advanced iOS Volume Three Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with macOS Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Server-Side Swift (Kitura Edition) Buy Beyond Code

Was this page useful? Let us know!

Average rating: 5.0/5