Skip to content
tezvyn:
Menu

Testing Async Code with XCTestExpectation

Source: developer.apple.comMediumHow cards are made

Testing Async Code with XCTestExpectation

XCTestExpectation is a stopwatch for your tests, pausing them to wait for asynchronous work like a network call to finish. Use it for testing code with completion handlers. The footgun is forgetting to fulfill() the expectation, causing a timeout failure.

Why it exists

Standard unit tests execute synchronously, finishing as soon as the last line of the function is run. This model breaks when testing asynchronous operations like network calls, as the test would finish and pass before the operation even completes. XCTestExpectation was created to bridge this gap, allowing a test to pause and wait for a result.

The mental model

Think of an XCTestExpectation as a promise you make to the test runner. You tell it, "I expect this specific event to happen." Then you start your asynchronous work. When that work finishes and calls its completion handler, you signal fulfill() on the expectation. The test runner waits a specified amount of time for that signal. If it gets the signal, the test proceeds. If the timeout is hit first, the test fails.

How it works

The process involves three key steps. First, you create an expectation with a clear description, like let expectation = self.expectation(description: "Fetch user profile"). Second, you call your asynchronous method. Inside its completion block, after you've run any assertions, you must call expectation.fulfill(). Third, the final line of your test method must be waitForExpectations(timeout: 5.0). This call blocks the test from finishing until either the expectation is fulfilled or the 5-second timeout expires.

When to use it

Use expectations for testing code that signals completion asynchronously. This is common with older callback-based APIs using completion handlers, the delegate pattern, or NotificationCenter. It's the standard tool for testing asynchronous code that doesn't use Swift's modern concurrency features.

When not to use it

Avoid expectations for purely synchronous code, as it adds needless complexity. For modern Swift code using async/await, you can often write simpler, more readable tests by marking the test function as async and using await on your asynchronous calls. This lets the test suspend and resume naturally without manual expectation management.

One canonical example

Imagine testing a NetworkClient with a function fetch(url:completion:). Your test would create an XCTestExpectation with the description "API fetch completes". It then calls client.fetch(...). Inside the completion handler, you'd assert that the result is successful and then call expectation.fulfill(). Crucially, you must also call fulfill() in the error case to prevent a timeout. The test concludes with waitForExpectations(timeout: 1.0).

Interview question

What is the primary reason XCTestExpectation is used in XCTest?

  • a.To convert asynchronous operations into synchronous ones for easier testing.
  • b.To automatically handle and report errors from network requests within tests.
  • c.To allow a test to pause and wait for an asynchronous event to complete before asserting results.Correct
  • d.To enable parallel execution of multiple asynchronous tests simultaneously.
Why?

Option C is correct because XCTestExpectation's core purpose is to bridge the gap between synchronous test execution and asynchronous code, ensuring the test waits for the async operation to signal completion. Option A is incorrect; it doesn't convert the operation itself, but rather makes the test wait for its asynchronous completion.

Just read this? Test yourself on what you have been reading.

Read the original → developer.apple.com

Put your scrolling time to good use

Learn one idea, try a quiz and save useful cards for revision. Tezvyn makes it easy to learn and stay current in your tech field, a few minutes at a time.

The iPhone app is on the way

We are building it. Until it lands, nothing here is held back from you: every interview card, your saved cards, streaks and the job board all work in Safari, plus hundreds of free practice quizzes of thirty questions each. Sign in and it all carries over to the app the day it arrives.

Want it as an icon? Tap Share at the bottom of Safari, then Add to Home Screen. It opens full screen and the cards you have read stay available offline.

Get it on Google PlayiPhone app coming soon

We are hiring for this. Open roles that interview on ios — each one lists the topics its interview covers.

See open roles