How I Test Functions Which Dispatch Tasks

Some functions dispatch tasks or code blocks to a different dispatch queue. I have wrapped the wait for that asynchronous code in an extension to XCTestCase.

This is a short one. I actually only want to share this little code snippet:

import XCTest

extension XCTestCase {
    ///
    /// Dispatches the fulfillment of an `XCTestExpectation` to the main dispatch queue and waits for it.
    ///
    /// This is supposed to be called after calling a function under test which dispatches tasks to the main dispatch queue.
    /// Otherwise tests would likely fail due to race conditions.
    /// Assertions would be executed and fail before the code under test has been executed.
    ///
    func waitForTasksDispatchedToMainQueue() {
        let expectation = expectation(description: "TasksDispatchedByFunctionUnderTestCompleted")

        // Because this dispatch is called after the code under test, it is added to the end of the main dispatch queue.
        DispatchQueue.main.async {
            // When the expectation is fulfilled, it can be assumed that all tasks dispatched by the code under test have completed.
            expectation.fulfill()
        }

        wait(for: [expectation], timeout: 1)
    }
}

This function is available conveniently in every test case function of tests derived from XCTestCase. In your test case, after calling code which is asynchronous or dispatching asynchronous code, this needs to be called like this before assertions can work:

func testSomething() {
    signIn() // Dispatches a Task or code block
    waitForTasksDispatchedToMainQueue()
    XCTAssertTrue(mockedDependency.signInCalled)
}

Note that this currently is designed for the main thread only.

About The Author

Peter Thomas Horn is a professional software developer at Open-Xchange specialized on the Apple platform. He previously worked a decade across the full stack of various web technologies. Originally started with Java on Windows at the age of 12 years. While staying humble in throwing around buzzwords like "VR" and "machine learning" he occasionally experiences problems and the fitting solutions considered worth sharing.