Testing: Recipes
Task-based patterns for common test scenarios. Unless stated otherwise, every recipe assumes the standard fixture:
Test a service that fires events
The key move is constructing the system under test with world.postboy - a real PostboyService records nothing.
Test a component that reacts to events
given.event overwrites the replay registration for its type and fires immediately; it suits pre-fired state. To deliver to listeners that are already subscribed, fire through the bus directly.
Assert payload predicates
For ordering or index access, read the history collection: world.history.messages(OrderPlacedEvent).all.
Stub executors and callback messages
Fixed answers - the common case:
Dynamic or failing stubs need the low-level mock. The action returns the result and may branch or throw:
Every stubbed interaction is still recorded - assert it with world.then.fired(GetTaxRateExecutor).once().
Assert that nothing happened
Write a strict-mode test
An unregistered type throws at first use, so missing wiring fails loudly instead of passing silently. given calls register too - world.given.executor(GetTaxRateExecutor, 0.2) replaces the explicit recordExecutor line.
Caveats and best practices
Dispose per test:
afterEach(() => world.dispose()). Reusing a world accumulates history and breakstimes/onceassertions.The SUT must receive
world.postboy; anything else records nothing.given.eventfires immediately and appears in history right away - relevant foronce/timescounts and for waiters withincludeHistory.execis synchronous - neverawaitit. For async results use callback messages withgiven.callbackorwaitForCallbackResult.Callback stubs answer via
finish(result). If nothing finishes the message, the caller's observable never emits andwaitForCallbackResulttimes out.Waiters ignore history unless
includeHistory: true.Prefer
givenoverworld.mocks; drop tomocksonly for dynamic or throwing stubs.Do not manually touch the internal
mock-namespace.../waiter-namespace...registrators - it breaksdispose().
Next steps
PostboyWorld: world anatomy and escape hatches.
Assertions: the full assertion API.
Async waiters: waiter semantics in detail.
Best practices: designing messages that are easy to test.