问题
I have a function that is a async wrapper around a synchronous function.
The synchronous function is like:
class Foo {
class func bar() -> [Int] {
return [1,2,3]
}
class func asyncBar(completion: @escaping ([Int]) -> Void) {
DispatchQueue.global(qos: .userInitiated).async {
let intArray = bar()
DispatchQueue.main.async {
completion(intArray)
}
}
}
}
When I call it from a XCTestCase
completion
does not run.
Is there some sort of perverse interaction between the way unit tests are done in XCode and the main thread?
I can find no documentation on the Web about this.
I have to use the main thread for the callback as it interacts with the Gui.
My test case looks something like:
func testAsyncBar() throws {
var run = true
func stopThisThing(ints: [Int]) {
run = false
}
Foo.asyncBar(completion: stopThisThing)
while run {
print ("Running...")
usleep(100000)
}
}
The busy loop at the end never stops.
回答1:
Your test’s while
loop will block the main thread (if the test runs on the main thread). As such, the closure dispatched via DispatchQueue.main.async
can never run, and your run
Boolean will never get reset. This results in a deadlock. You can confirm this by printing Thread.isMainThread
or adding a test like dispatchPrecondition(condition: .onQueue(.main))
.
Fortunately, unit tests have a simple mechanism that avoids this deadlock.
If you want unit test to wait for some asynchronous process, use an XCTestExpectation
:
func testAsyncBar() throws {
let e = expectation(description: "asyncBar")
func stopThisThing(ints: [Int]) {
e.fulfill()
}
Foo.asyncBar(completion: stopThisThing)
waitForExpectations(timeout: 5)
}
This avoids problems introduced by the while
loop that otherwise blocked the thread.
See Testing Asynchronous Operations with Expectations.
来源:https://stackoverflow.com/questions/65572514/using-dispatch-main-in-code-called-from-xctestcase-does-not-work