Postboy Help

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:

const msg = world.then.fired(MyEvent).value;

You get the most recently emitted message of that type.
If you expect the earliest one, use world.history.messages(MyEvent).first instead.

const firstMsg = world.history.messages(MyEvent).first; expect(firstMsg.payload).toBe(expected);

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:

service.run(); // fires event const event = await world.waiter.waitFor(MyEvent, { includeHistory: true });

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:

import { PostboyWorldVerifier } from '@artstesh/postboy-testing'; const verifier = new PostboyWorldVerifier(world.history); const wasFired = verifier.fired(SomeEvent);

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:

afterEach(() => { world.dispose(); });

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

.value gives last message

Use history.messages(type).first for the first

given.event fires instantly

Accept or use an async scenario with waiter if timing matters

Waiter ignores history by default

Pass includeHistory: true when needed

waitForNone pauses the test

Set a small timeout

dispose() forgotten

Always add afterEach(… dispose …)

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.

07 мая 2026