UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

How to test asynchronous functions using expectation()

Swift version: 5.6

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

    // 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.

Hacking with Swift is sponsored by RevenueCat

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.

Learn more here

Sponsor Hacking with Swift and reach the world's largest Swift community!

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 Pro Swift Buy Pro SwiftUI 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 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 Beyond Code

Was this page useful? Let us know!

Average rating: 5.0/5

Unknown user

You are not logged in

Log in or create account

Link copied to your pasteboard.