Nathan Hosselton
Nathan Hosselton

Reputation: 1335

Using XCTFail when waiting on an expectation does not prevent timeout

When running an XCTest of an asynchronous operation, calling XCTFail() does not immediately fail the test, which was my expectation. Instead, whatever timeout period is remaining from the call to wait is first exhausted, which unnecessarily extends the test time and also creates a confusing failure message implying the test failed due to timeout, when in fact it explicitly failed.

func testFoo() {
    let x = expectation(description: "foo")

    DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
        XCTFail("bar")
    }

    wait(for: [x], timeout: 5)
}

In the above example, although the the failure occurs after roughly 2 seconds, the test does not complete until the timeout period of 5 seconds has elapsed. When I first noticed this behavior I thought I was doing something wrong, but this seems to just be the way it works, at least with the current version of Xcode (9.2).

Since I didn't find any mention of this via google or stackoverflow searches, I'm sharing a workaround I found.

Upvotes: 2

Views: 4752

Answers (1)

Nathan Hosselton
Nathan Hosselton

Reputation: 1335

I discovered that the XCTestExpectation can still be fulfilled after calling XCTFail(), which does not count as a pass and immediately expires the wait. So, applying this to my initial example:

DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
    XCTFail("bar")
    x.fulfill()
}

This may be what Apple expects but it wasn't intuitive to me and I couldn't find it documented anywhere. So hopefully this saves someone else the time that I spent confused.

Upvotes: 9

Related Questions