Article

Testing Asynchronous Operations with Expectations

Verify that asynchronous operations behave as expected.

Overview

Asynchronous operations are operations that do not execute directly within the current flow of code. This might be because they run on a different thread, in a delegate method, or in a callback.

To test that asynchronous operations behave as expected, you create one or more expectations within your test, and then fulfill those expectations when the asynchronous operation completes successfully. Your test method waits until all expectations are fulfilled or a specified timeout expires.

Listing 1

Testing that data is downloaded successfully on a background thread

func testDownloadWebData() {
    
    // Create an expectation for a background download task.
    let expectation = XCTestExpectation(description: "Download apple.com home page")
    
    // Create a URL for a web page to be downloaded.
    let url = URL(string: "https://apple.com")!
    
    // Create a background task to download the web page.
    let dataTask = URLSession.shared.dataTask(with: url) { (data, _, _) in
        
        // Make sure we downloaded some data.
        XCTAssertNotNil(data, "No data was downloaded.")
        
        // Fulfill the expectation to indicate that the background task has finished successfully.
        expectation.fulfill()
        
    }
    
    // Start the download task.
    dataTask.resume()
    
    // Wait until the expectation is fulfilled, with a timeout of 10 seconds.
    wait(for: [expectation], timeout: 10.0)
}

Listing 1 creates a new instance of XCTestExpectation. Then, it uses URLSession's dataTask(with:) method to create a background data task that downloads the apple.com home page on a background thread. After starting the data task, the main thread waits for the expectation to be fulfilled, with a timeout of ten seconds.

When the data task completes, its completion handler verifies that the downloaded data is non-nil, and fulfills the expectation by calling its fulfill() method to indicate that the background task completed successfully.

The fulfillment of the expectation on the background thread provides a point of synchronization to indicate that the background task is complete. As long as the background task fulfills the expectation within the ten second timeout, this test method will pass.

There are two ways for the test to fail:

  1. The data returned to the completion handler is nil, causing XCTAssertNotNil(_:_:file:line:) to trigger a test failure.

  2. The data task does not call its completion handler before the ten second timeout expires, perhaps because of a slow network connection or other data retrieval problem. As a result, the expectation is not fulfilled before the wait timeout expires, triggering a test failure.

See Also

First Steps

class XCTestExpectation

An expected outcome in an asynchronous test.