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.
2
Answers
Solution: Change
wait(for:)
toawait 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
andwait(for:)
is not.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 notasync
— that is, it doesn’t make anyawait
calls to anasync
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 notasync
. That is why, when you take away theasync
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: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 to2
, 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 wordawait
already waits: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 isasync
, so it remains unclear why we here in the first place.