Skip to content

3.3.0

Latest

Choose a tag to compare

@NikSativa NikSativa released this 14 Oct 16:55

Adds primitives for testing concurrent code deterministically: a test can wait until a fake is called and decide when and how each async call finishes, instead of sleeping and hoping the work is done.

stub(_:).andWaitForResponse() keeps every call of a stubbed async function waiting and returns a StubResponder. Each respond(with:), respond() or respond(throwing:) answers exactly one call, the oldest waiting call first. A response given before the call arrives is kept for it, so responses can be prepared in advance.

waitForCall(_:withArguments:times:) suspends until the fake has recorded enough matching calls, counting calls made before the wait. It is available on instances and on types, and throws CancellationError when the waiting task is cancelled. It never ends on its own, so pair it with a time limit such as Swift Testing's .timeLimit(_:).

spryifyAsync() and spryifyAsyncThrows() are the async counterparts of spryify() and spryifyThrows(), with fallbackValue: variants and class-level versions. They record the call before the stub answers it, and a call takes its place in the responder's queue before it is recorded: once waitForCall() returns, the call is already waiting, and responses reach calls in the order the calls arrived.

A waiting call of a throwing function ends with CancellationError when its task is cancelled, so cancelling the code under test releases it. A non-throwing function cannot report cancellation, so its call keeps waiting until the test answers it. A synchronous function stubbed with andWaitForResponse() traps with a message pointing at spryifyAsync().

@SpryableFunc on an async function and @SpryableVar(.async) now generate await spryifyAsync(...) or try await spryifyAsyncThrows(...). Immediate stubs answer exactly as before; the only difference is that such a call is recorded before its stub is resolved.

Dependencies are unchanged: Threading 2.3.4.