In this episode we cover the concept of expectations, which enables us to test asynchronous code, properly timing out and failing a test if the expectation is never fulfilled.
Testing Asynchronous Code Let’s assume we have some code we want to test but the work is done asynchronously and we are called back when the work is completed. For the sake of this example, we can create a dummy class that simulates this type of behavior. import XCTest class Job { var finished = false func run(completion: @escaping ()->Void) { DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { self.finished = true completion() } } } Testing Async Code Directly If we run write a test that runs the code above and asserts that it completed, then the test will fail… Using Expectations To get around this we want our tests to wait a bit before running the assertions. We also need a way to time out and fail if the code never completes. We can use the expectation method on XCTestCase which gives us a handle that we can wait on class TestAsyncCode : XCTestCase { func testJobFinishes() { let exp = expectation(description: "Completion block called") // ... } } Then we can fulfill this expectation when the callback is completed, signaling to the framework that we don’t need to wait any longer class TestAsyncCode : XCTestCase { func testJobFinishes() { // ... job.run { exp.fulfill() // ... } } } Finally we can tell the test to wait for up to 3 seconds for this expectation to be met: class TestAsyncCode : XCTestCase { func testJobFinishes() { let exp = expectation(description: "Completion block called") let job = Job() job.run { exp.fulfill() XCTAssertTrue(job.finished) } waitForExpectations(timeout: 3, handler: nil) } }