Postboy Help

Mock Setup with `given`

world.given is the Arrange phase API.
It lets you prepare the mock environment before your code acts — send initial events, set up pre‑canned callback responses, and mock executor return values.

Access it from the world:

world.given .event(new UserLoggedIn(user)) .callback(LoadCartQuery, mockCart) .executor(ValidateOrderExecutor, true);

All given methods return the same PostboyGivenService instance, so you can chain them in a natural, readable sequence.

Why use given?

Without given, you’d have to manually configure low‑level mock streams via world.mocks.
given hides that complexity, giving you an expressive vocabulary for the Arrange phase.

Goal

given method

Fire a pre‑event before the test action

given.event()

Respond to a callback with a fixed value

given.callback()

Return a stubbed value from an executor

given.executor()

Under the hood, given delegates to world.mocks (PostboyMessageStreamService), so the mock bus is fully wired and ready.

given.event(message) – fire an event upfront

Sometimes your code expects a previous event to have occurred — for example, a “user logged in” event before the action under test.
given.event() sends that event immediately to the mock bus.

world.given.event(new UserLoggedInEvent(42));

What happens internally:

  1. A replay subject for the message type is registered (so any future subscriber will also receive this event).

  2. The message is fired through world.postboy.fire().

  3. The message appears in the recorded history, just like any other fired message.

TesterTesterworld.givenworld.givenworld.mocksworld.mocksworld.postboyworld.postboy.event(new UserLoggedIn(42))mockEvent(message)register replay + fire(message)write to history

given.callback(Type, result) – mock a callback response

When your code uses PostboyCallbackMessage<T> to request data, you can pre‑define the answer.

world.given.callback(FetchUserQuery, fakeUser);

What happens internally:

  1. A subject for the callback message type is registered.

  2. The mock subscribes to that message type.

  3. When your code sends the callback via fireCallback, the mock intercepts it and calls message.finish(result), resolving the caller’s observable.

TesterTesterSUTSUTworld.postboyworld.postboyworld.givenworld.givenCallbackMockCallbackMock.callback(FetchUserQuery, fakeUser)register subject + subscribetrigger()fireCallback(new FetchUserQuery())intercept messagemessage.finish(fakeUser)observable emits fakeUser

Example in a test:

import {Forger} from "@artstesh/forger"; import {firstValueFrom} from "rxjs"; class UserProfileComponent { constructor(private postboy: PostboyService) { } async loadUser(): Promise<User> { const result = this.postboy.fireCallback(newFetchUserQuery()); return firstValueFrom(result); } } it('loads user', async () => { const fakeUser = Forger.create<User>()!; world.given.callback(FetchUserQuery, fakeUser); const component = new UserProfileComponent(world.postboy); // const user = await component.loadUser(); // should().object(user, fakeUser).equal(); });

Important: The mock answers every matching callback message sent during the test. If you need different answers for different calls, you may need to use the low‑level world.mocks.mockCallback() with a custom action function.

given.executor(Type, result) – mock an executor return value

Executors (PostboyExecutor<T>) are synchronous calls that return a value. With given.executor(), you stub that return value.

world.given.executor(GetTaxRateExecutor, 0.19);

What happens internally:

  1. The executor is registered with a handler function that simply returns result.

  2. When your code calls postboy.exec(new GetTaxRateExecutor()), the mock handler is invoked and returns the stub value.

TesterTesterSUTSUTworld.postboyworld.postboyworld.givenworld.givenExecutorRegistryExecutorRegistry.executor(GetTaxRateExecutor, 0.19)recordExecutor(type, () => 0.19)calculate()exec(new GetTaxRateExecutor())call handler0.190.19

Example:

class TaxCalculator { constructor(private postboy: PostboyService) { } calculate(amount: number): number { const rate = this.postboy.exec(new GetTaxRateExecutor()); return amount * rate; } } it('calculates tax', () => { world.given.executor(GetTaxRateExecutor, 0.2); const calc = new TaxCalculator(world.postboy); // const result = calc.calculate(100); // should().number(result).equals(20); });

Note that the executor itself is also recorded in history, so you can verify it was called with then.fired(GetTaxRateExecutor).

Chaining given methods

Because every method returns the same PostboyGivenService instance, you can build a complete initial state in a single statement.

world.given .event(new UserLoggedInEvent(1)) .event(new CartCreatedEvent(2)) .callback(FetchCartQuery, { items: [] }) .executor(GetDiscountExecutor, 0.1);

This reads clearly as a scenario: “Given user logged in, a cart was created, fetching the cart returns empty, and the discount is 10%.”

Relationship to world.mocks

As noted, given is a high‑level wrapper around PostboyMessageStreamService (world.mocks). For most tests, given is sufficient. You only need world.mocks directly if you require:

  • Conditional mock behaviour depending on the message payload.

  • Multiple different answers for the same callback type.

  • Custom logic before calling finish(result).

  • Mocks that throw errors.

In those rare cases, refer to the advanced mocks section.

Best practices

  • Use given as the first block in your test — it sets the stage.

  • Chain related setups to make the test context clear.

  • Don’t over‑mock — only set up what the test actually needs.

  • Remember that given.event() fires immediately — it appears in history right away, not when the action happens.

  • Check that your executor stubs match the expected return type — TypeScript will help, but runtime mismatches can cause surprises.

Next steps

With given you’ve mastered the Arrange phase. Now learn how to verify outcomes and handle asynchronous scenarios:

07 мая 2026