Postboy Help

PostboyWorld

PostboyWorld is the central facade of @artstesh/postboy-testing.
It assembles and coordinates every component you need to test event‑driven code — a mock bus, a history log, an async waiter, and expressive BDD‑style APIs.

You never interact with individual testing services directly. You create one PostboyWorld and everything is ready to go.

import {PostboyWorld} from '@artstesh/postboy-testing'; const world = new PostboyWorld();

Constructor & settings

PostboyWorld accepts an optional PostboyTestingSettings object.

interface PostboyTestingSettings { strict: boolean; }

Option

Type

Default

Description

strict

boolean

false

When true, every message type must be explicitly registered in the registry before use.

Non‑strict mode (strict: false) is the recommended starting point.
Strict mode (strict: true) gives you extra safety — it catches missing registrations early, mirroring a more disciplined production setup.

// Non-strict: messages work out of the box const world = new PostboyWorld({strict: false}); // Strict: requires explicit registration const strictWorld = new PostboyWorld({strict: true}); strictWorld.registry.recordSubject(MyEvent);

Internal composition

When you instantiate PostboyWorld, it creates and wires together six internal services:

PostboyWorldPostboyServiceMockMessageHistoryPostboyWaiterServicePostboyGivenServicePostboyThenServicePostboyMessageStreamServiceRegistry (namespace)records viafire / exec / fireCallback / subfire event / callback / executordelegates mock setupreads for assertionssubscribes for future messagesoptional check (includeHistory)registers types (strict mode)

You access these pieces through public properties on the world. Everything shares a consistent state, so you never need to pass dependencies around manually.

Properties overview

world.postboy

The mock version of PostboyService. Use it exactly like you would use the real bus in production — inject it into your services, call fire(), exec(), fireCallback(), sub(), and once().
Behind the scenes it writes every action into the shared MessageHistory.

class MyService { constructor(private postboy: PostboyService) { } } const service = new MyService(world.postboy); service.doSomething(); // Every message emitted is now recorded.

world.history

Direct access to the history of fired messages, executors, callbacks, and subscriptions.
Organised by message type, it provides HistoryCollection<T> objects with methods like first, last, all, has(predicate), and length.

const orders = world.history.messages(OrderPlacedEvent).all; const subCount = world.history.subs(OrderPlacedEvent);

Most of the time you’ll use world.then for assertions, but history is available for custom validation when the declarative API isn’t enough.

world.waiter

The PostboyWaiterService for asynchronous test scenarios.
It returns promises that resolve when a message arrives (or reject after a timeout). Essential for testing messages emitted inside subscriptions, timeouts, or microtasks.

service.triggerAsync(); const event = await world.waiter.waitFor(OrderPlacedEvent, { where: e => e.orderId === 123, timeout: 2000, includeHistory: true });

world.given

BDD‑style API for the Arrange phase.
Use it to prepare the mock environment: send a pre‑event, mock a callback result, or mock an executor return value.
All methods return this, enabling chaining.

world.given .event(new UserLoggedInEvent(user)) .callback(LoadUserQuery, fakeUser) .executor(ValidateOrderExecutor, true);

Under the hood, given uses the low‑level world.mocks service, but the higher‑level API keeps your tests readable.

world.then

BDD‑style API for the Assert phase.
Declaratively verify what your code communicated.
Reads from the same MessageHistory as world.history but throws clear, descriptive errors when expectations aren’t met.

world.then .fired(OrderPlacedEvent).once() .with(event => event.amount > 0) .and() .subscribed(OrderPlacedEvent).atLeast(1);

world.mocks

Low‑level PostboyMessageStreamService.
While most everyday needs are covered by world.given, world.mocks gives you direct control over mock streams.
Use it only for advanced scenarios.

world.mocks.mockEvent(new SomeEvent()); world.mocks.mockCallback(SomeCallback, msg => fakeResult); world.mocks.mockExecute(SomeExecutor, exec => fakeResult);

world.mocks is automatically disposed when you call world.dispose(), so manual cleanup is not required.

world.registry

The PostboyAbstractRegistrator tied to the mock namespace.
When running in strict mode, you must register message types explicitly here.

world.registry.recordSubject(MyEvent); world.registry.recordReplay(MyCallback, 1); world.registry.recordExecutor(MyExecutor, handlerFn);

Lifecycle & dispose()

A PostboyWorld creates internal subscriptions and a dedicated namespace inside the mock bus.
If you never call dispose(), these resources leak between tests — leading to stale state, cross‑test interference, and difficult‑to‑debug failures.

Always pair the world with beforeEach/afterEach:

let world: PostboyWorld; beforeEach(() => { world = new PostboyWorld({strict: false}); }); afterEach(() => { world.dispose(); // ← mandatory });

dispose() clears the message history, unsubscribes from all streams, and removes the namespace.
This guarantees that every test starts from a completely clean slate.

Rapid example

it('verifies event after action', () => { // Arrange const service = new MyService(world.postboy); world.given.event(new UserLoggedInEvent(100)); // set state // Act service.performAction(); // Assert world.then .fired(ActionCompletedEvent) .once(); });

The pattern is consistent across every test. Set up with given, act with your service, and verify with then or waiter. Everything revolves around PostboyWorld.

What’s next?

07 мая 2026