Swift version: 5.10
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.
SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure and A/B test your entire paywall UI without any code changes or app updates.
Sponsor Hacking with Swift and reach the world's largest Swift community!
Available from iOS
This is part of the Swift Knowledge Base, a free, searchable collection of solutions for common iOS questions.
Link copied to your pasteboard.