Some asynchronous tests are difficult to test because of how the Swift runtime processes suspension points. withMainSerialExecutor attempts to run all tasks spawned within the provided operation serially and deterministically. This makes asynchronous tests faster and significantly more reliable.
Warning: This API is intended only for use in tests. Do not use it in application code. It relies on a global, mutable variable in the Swift runtime and provides no scoping guarantees if that variable changes during the operation.
When using this in tests, you may need to insert a Task.yield() in your dependency endpoints to prevent the compiler from inlining async closures that don't perform actual async work.
func testIsLoading() async {
await withMainSerialExecutor {
let model = NumberFactModel(getFact: {
await Task.yield() // Required to prevent inlining
return "\($0) is a good number."
})
let task = Task { await model.getFactButtonTapped() }
await Task.yield()
XCTAssertEqual(model.isLoading, true)
XCTAssertEqual(model.fact, nil)
await task.value
XCTAssertEqual(model.isLoading, false)
XCTAssertEqual(model.fact, "0 is a good number.")
}
}