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.