Postboy Help

Quick Start

Get your first @artstesh/postboy test running in under two minutes.
This guide assumes you’re using a test runner like Jest, Vitest, Karma, or Jasmine.

1. Installation

Add the testing toolkit as a development dependency.
It automatically brings in the core @artstesh/postboy package, so you don’t need to install it separately unless you’ve pinned a specific version.

npm install --save-dev @artstesh/postboy-testing

or

yarn add -D @artstesh/postboy-testing

Branch

@artstesh/postboy

1.x

1.x

3.x

3.x

2. Define your message

For a fire‑and‑forget event, extend PostboyGenericMessage and give it a static ID.

import {PostboyGenericMessage} from '@artstesh/postboy'; export class OrderPlacedEvent extends PostboyGenericMessage { static readonly ID = 'order-placed'; constructor(public readonly orderId: number) { super(); } }

You can also use PostboyCallbackMessage<T> for request‑response or PostboyExecutor<T> for synchronous operations — they all work with the testing tools in exactly the same way.

3. Create the world

PostboyWorld is the central testing fixture. Set it up in beforeEach and tear it down with dispose() in afterEach.

import {PostboyWorld} from '@artstesh/postboy-testing'; let world: PostboyWorld; beforeEach(() => { world = new PostboyWorld({strict: false}); }); afterEach(() => { world.dispose(); });

strict: false lets you use messages without pre‑registering them — perfect for getting started. Later, you can switch to strict: true to enforce explicit registration and catch configuration errors.

4. Write the code under test

Any class that depends on PostboyService will accept the mock version world.postboy. Here’s a simple service that fires an event.

import {PostboyService} from '@artstesh/postboy'; export class OrderService { constructor(private readonly postboy: PostboyService) { } placeOrder(orderId: number): void { // … domain logic … this.postboy.fire(new OrderPlacedEvent(orderId)); } }

5. Write your first test

Instantiate the service with world.postboy, call the method, and then assert what was communicated.

import { Forger } from '@artstesh/forger'; it('fires OrderPlacedEvent when an order is placed', () => { const service = new OrderService(world.postboy); const orderId = Forger.create<number>()!; // service.placeOrder(orderId); // world.then .fired(OrderPlacedEvent) .once() .with(event => event.orderId === orderId); });
  • world.then.fired(Type) returns an assertion builder for that message type.

  • .once() verifies the message was sent exactly once.

  • .with(predicate) checks that at least one message satisfies the predicate (the last one is also accessible via .value).

If the message hasn’t been fired, .fired(…) will throw a clear error immediately, pointing you directly to the failing test.

6. Async? No problem

When your code publishes events inside a subscription, microtask, or timeout, use world.waiter to wait for them.

import { Forger } from '@artstesh/forger'; import { should } from '@artstesh/it-should'; it('waits for async event', async () => { const service = new OrderService(world.postboy); const orderId = Forger.create<number>()!; // Imagine placeOrder triggers some async work… void service.placeOrderAsync(orderId); const event = await world.waiter.waitFor(OrderPlacedEvent); // should.number(event.orderId).equals(orderId); });

The waiter subscribes to future messages.
If the event might have already been sent before waitFor is called, add includeHistory: true.

Where to go from here

You’re now ready to test every part of your event‑driven application — from simple events to complex callback orchestrations.

06 мая 2026