Low‑Level `mocks` (`world.mocks`)
While world.given covers most Arrange‑phase needs, there are times when you need finer control over mock behaviour — conditional responses, multiple different answers for the same callback, or mocks that throw errors. That’s where world.mocks steps in.
world.mocks is a PostboyMessageStreamService instance. It exposes three core methods that let you manipulate the mock bus at the stream level.
Access it directly from the world:
Why mocks exists
world.given is built on top of world.mocks. Every call to given.event(), given.callback(), or given.executor() internally delegates to mocks, wrapping the details in a simpler API.
Using mocks directly lets you escape the “one answer per type” limitation.
Use mocks when you need to:
Return different values for successive calls to the same callback or executor.
Perform an assertion or side effect inside the mock handler.
Throw an error from a callback or executor.
Dynamically compute the result based on the message payload.
For straightforward fixed‑value mocks, stick to world.given — it keeps your tests readable.
mocks.mockEvent(message)
Registers a replay subject for the message type and immediately fires the given message.
This is exactly what given.event(new UserLoggedInEvent(42)) does. You can use either, but mockEvent is available when you’re already working at the mocks level.
mocks.mockCallback(type, action)
The most powerful mock method for callbacks. It registers a subject for the given PostboyCallbackMessage<T> subclass and subscribes with a handler action.
When a callback message arrives, the action receives the message and must call message.finish(result) (or perform any custom work).
Compare with given.callback
given.callback(FetchCartQuery, fixedResult)always returnsfixedResult.mocks.mockCallback(...)gives you the full message object and full control.
Dynamic behaviour example
Now the first call returns ['apple'], the second returns ['banana'].
mocks.mockExecute(type, action)
Registers an executor handler. Similar to mockCallback, but for synchronous PostboyExecutor<T>.
Throwing an error
Using given.executor you can only return a value; with mocks.mockExecute you can also simulate failures.
How mocks relates to given
given is a facade over mocks. Whenever you need more than a fixed answer, drop down to mocks — the rest of the test stays the same.
Lifecycle and cleanup
All mocks registered via world.mocks are automatically torn down when you call world.dispose(). You do not need to manually unsubscribe or clean them up.
If for some reason you need to remove mocks within a single test, call world.mocks.dispose() — but this is rarely necessary and will clear all stream registrations.
Best practices
Prefer
givenfor fixed responses — it’s shorter and expresses intent clearly.Use
mocksfor dynamic or conditional behaviour thatgivencan’t express.Keep mock logic simple — if your mock handler grows large, consider refactoring the production code to be more testable.
Remember that
mockEventfires immediately — just likegiven.event.You can mix
givenandmocks— they share the same underlying registry and history.
Next steps
With mocks you’ve seen the full flexibility of the testing toolkit. Now explore:
Async testing with
waiter– wait for messages fired by these mocks.Then: assertions – verify that your mocks were used correctly.
Recipes – advanced patterns combining
given,mocks,waiter, andthen.