Testing: Async Waiters
When a message is emitted inside a subscription, a microtask, or a timer, the history is not filled yet at assertion time. world.waiter solves this with promise-based waits over the live message stream. Every method rejects with a descriptive Error on timeout; the default timeout is 1000 ms.
WaitOptions
waitForMany adds exact?: boolean; waitForNone adds timeoutMessage?: string.
waitFor - one future message
Resolves with the first future message matching where (any message if omitted). With includeHistory: true the waiter first scans recorded messages and resolves immediately on a hit.
waitForMany - several messages
With exact: true the waiter must prove the count is precise, so it always waits the full timeout before resolving; it rejects on both under- and over-count. Omit exact when speed matters.
waitForAny - race several types
Resolves with the first matching message of any of the given types. The options apply to the raced types as one group; for per-type decisions, inspect the resolved instance after the await.
waitForCallbackResult - callback outcome
Resolves with the result of the next message.finish(result) for that callback type - or with an already-recorded result when includeHistory: true. If nothing ever calls finish, the wait times out. Callback stubs usually finish synchronously, so when you await only after the callback has already completed, pass includeHistory: true - otherwise the waiter keeps waiting for a next finish that never comes.
waitForNone - assert silence
Resolves only after the timeout passes without a matching message; rejects immediately when one fires, or when one was already recorded and includeHistory: true is set. timeoutMessage overrides the rejection text.
delay - plain sleep
Not an assertion. Prefer a specific wait whenever an expected message exists.
Semantics
Method | Resolves with | Resolves when | Rejects when |
|---|---|---|---|
| the message | first future match of | timeout without a match |
|
|
| timeout with fewer matches; with |
| the message | first match across all raced types | timeout without any match |
| the result | next | timeout without a |
|
| timeout elapsed in silence | a matching message fires |
|
| after | never |
Pitfalls
includeHistorydefaults tofalse. A message fired before the wait is invisible without it - the most common cause of surprising waiter timeouts.waitForManywithexact: truealways burns the full timeout, even when the count already looks right. Keep the timeout small.waitForNoneadds its whole timeout to the test duration. Use the smallest safe value.Waiters clean up their own subscriptions when they settle, but
world.dispose()inafterEachis still mandatory.
Next steps
Assertions: verify the history once the wait resolved.
Recipes: complete async test scenarios.
PostboyWorld: the world fixture and dispose discipline.