Consider this simple test case
func test_Example() async {
let exp = expectation(description: "It's the expectation")
Task {
print("Task says: Hello!")
exp.fulfill()
}
wait(for: [exp], timeout: 600)
}
When I run this on an actual device, the test passes. However, when I run it on a simulator, it hangs and never completes.
Removing the async keyword from the function declaration solves the issue and the test passes on a simulator as well.
My actual use case / tests require the test to be async, so removing it is not really an option.
CodePudding user response:
The mistake here doesn't really have anything to do with testing; it seems to have to do with what async/await is.
In your "simple test case", you have mistakenly added async to a method that is not async — that is, it doesn't make any await calls to an async method. This has nothing to do with tests, really; it's just a wrong thing to do in general, even though the compiler does not help you by warning you about it.
On the contrary, the chief purpose of a Task block is usually to let you call an async method in a context that is not async. That is why, when you take away the async designation, everything goes fine; you are behaving a lot more correctly.
However, the example still suffers from the problem that you aren't doing anything async even inside the Task. A more appropriate use of a Task would look more like this:
func test_Example() {
let exp = expectation(description: "It's the expectation")
Task {
try await Task.sleep(for: .seconds(3))
print("Task says: Hello!")
exp.fulfill()
}
wait(for: [exp], timeout: 4)
}
I have deliberately configured my numbers so that we are actually testing something here. This test passes, but if you change the 4 in the last line to 2, it fails — thus proving that we really are testing whether the Task finished in the given time, and failing if it doesn't.
If you're going to mark a test as async, then you would not use a Task inside it; you are already in a Task! But if we do that, then there is no need to wait for any expectations at all; the word await already waits:
func test_Example() async throws {
print("Task starts")
try await Task.sleep(for: .seconds(3))
print("Task says: Hello!") // three seconds later
}
But at this point we are no longer testing anything. And that's sort of the point; there isn't anything about your original async example that is async, so it remains unclear why we here in the first place.
CodePudding user response:
Solution: Change wait(for:) to await waitForExpectations(timeout:)
I don't know why it works, but probably it has something to do with the limited amount of threads available in a simulator.
waitForExpectations(timeout:) is marked with @MainActor and wait(for:) is not.
