Caveats
@artstesh/postboy-testing is designed to be intuitive, but a few implementation details can lead to surprising behaviour if you aren’t aware of them.
This section catalogues those “gotchas” so you can avoid them.
1. then.fired(…).value returns the last message, not the first
When you write:
You get the most recently emitted message of that type.
If you expect the earliest one, use world.history.messages(MyEvent).first instead.
2. given.event(message) fires immediately
world.given.event(new SomeEvent()) sends the event to the bus during the Arrange phase — not later, not deferred.
It appears in the history straight away.
If you use includeHistory: true with waitFor, that event will be found.
If you use waitFor without history, the waiter will only see future messages, ignoring this pre‑sent event.
3. waitFor, waitForMany and waitForAny ignore history by default
All waiter methods default to includeHistory: false.
If your code has already emitted a message before you call waitFor, that message is invisible to the waiter unless you explicitly opt in:
Without includeHistory: true the waiter will timeout, even though the event is sitting in the history.
4. waitForNone is satisfied only after timeout
waitForNone does not return immediately when the test reaches it.
It subscribes to the bus and waits for the given timeout (default 1000ms).
If no matching message arrives during that window, the promise resolves.
If one does arrive, the promise rejects.
This means a waitForNone call always adds the timeout duration to your test execution.
Set timeout to the smallest value you can safely tolerate.
5. PostboyWorldVerifier is not exposed by PostboyWorld
PostboyWorldVerifier is exported from the package, but PostboyWorld does not provide a property for it.
If you want a boolean‑style verifier (returning true/false instead of throwing), you must create it manually:
In most tests you won’t need it — world.then is the recommended API.
6. dispose() is mandatory
PostboyWorld creates subscriptions and a dedicated namespace.
If you skip dispose(), those resources leak into subsequent tests, causing cross‑test contamination — messages from one test can be seen by another, and subscriptions accumulate.
Always use:
No exceptions.
7. Strict mode requires explicit registration
When you set { strict: true }, every message type must be registered in world.registry or with the given mechanism before use.
Forgetting a registration causes a runtime error from the underlying PostboyService.
If you’re migrating from non‑strict, add the necessary recordSubject, recordReplay, or recordExecutor calls to your beforeEach block.
8. Callback mocks only answer when finish() is called
given.callback(type, result) works only if the production handler actually invokes message.finish(result).
If your handler never calls finish, the mock passes silently and the caller’s observable never emits.
The waiter.waitForCallbackResult for that type will eventually timeout.
9. Mock subscriptions are shared across the test
The mock bus is a full implementation — not a shallow stub.
If your code subscribes multiple times, all those subscriptions are active and will receive messages.
This is usually what you want, but keep it in mind when reasoning about test behaviour (e.g., a message may be logged by two different listeners).
Quick reference
Surprise | What to do |
|---|---|
| Use |
| Accept or use an async scenario with |
Waiter ignores history by default | Pass |
| Set a small |
| Always add |
Knowing these details will save you from puzzling test failures.
When in doubt, refer back to this page — it covers the edge cases the rest of the documentation assumes you already understand.