This question is somewhat related to Reliably testing code that adopts Swift Concurrency? - #67 by stephencelis, but enough of a variation to warrant it's own thread.
I'm trying to determine the most elegant approach to awaiting an asynchronous bit of code to execute, to verify that it is indeed triggered.
For example, we have a ViewModel with a few properties that should trigger a network call on didSet
, which is a synchronous context. We are ultimately using a Task
to call an async service method (which is mocked, for the purposes of the test).
The mocked service uses an expectation to verify that it is called, but this doesn't reliably get called if I don't await the task somehow.
I've taken to exposing the Task?
property on the ViewModel so that I can await it immediately after setting one of the properties that will initiate the didSet
in the unit test. This feels clumsy to me.
In another scenario, I actually @Published
the Task?
property so I could use .values
to create an AsyncPublisher
and use a for await
loop to wait for a non-nil Task so the test was a little bit more bulletproof against timing issues. That won't work for @MainActor
objects where I want to clean up the task in deinit
, because marking the property as @Published
triggers a compiler error about mutating a actor property in a non-isolated thread when I do deinit { task?.cancel() }
. I'm not sure why. I'm not sure if it even should be working without being published: Deinit and MainActor - #26 by bobspryn