For testing purposes, I have the following test function:
func test_wait() {
var string: String?
DispatchQueue.main.async {
string = "set"
print("string set")
}
let notNilPredicate = NSPredicate(format: "self != nil")
let notNilExpectation = expectation(for: notNilPredicate, evaluatedWith: string)
print("start waiting")
let waitResult = XCTWaiter.wait(for: [notNilExpectation], timeout: 5)
XCTAssert(waitResult == .completed, "wait for notNilExpectation failed with result \(waitResult)")
}
This test fails.
The wait result is .timedOut
, and the log is
start waiting
string set
… : XCTAssertTrue failed - wait for notNilExpectation failed with result XCTWaiterResult(rawValue: 2)
Interrupting test
I do not understand why the wait fails although var string
is set.
However, the test succeeds when I out comment DispatchQueue.main.async
, i.e. when I execute its block synchronously. The log is then
string set
start waiting
Test Case '-[ShopEasyTests.CoreDataCloudKitContainerTest test_wait]' passed (1.087 seconds).
To my understanding, the async version of the test function should also work.
What is wrong?
It's the predicate (and the semantics of capture). You think you are handing a reference to string
into your predicate evaluation, but you aren't; you are just passing nil
, and nil
is not going to magically be not-nil
any time soon. To confuse yourself less, rewrite as
let notNilPredicate = NSPredicate {_,_ in string != nil }
let notNilExpectation = expectation(for: notNilPredicate, evaluatedWith: nil)