Testing: PostboyWorld
PostboyWorld is the single fixture of @artstesh/postboy-testing. It constructs a recording mock bus, a message history, stubbing, assertion, and waiter services, plus a private namespace registrator, and wires them together. Tests never build these pieces manually; they read them off the world through getters.
Anatomy
Getter | Type | Purpose |
|---|---|---|
|
| The mock bus. Extends the real |
|
| Recorded messages, callback results, and subscription counters. |
|
| Arrange-phase stubs: |
|
| Assert-phase checks: |
|
| Async waits: |
|
| Low-level stream mocks; the layer |
|
| Namespaced registrator for manual registration. |
Strict vs non-strict
strict: false(default): the mock auto-registers unseen message IDs with a dummy subject and unseen executors with a null-returning function. The SUT can fire and exec without prior setup.strict: true: real-bus behavior. Fire or sub of an unregistered type throws, so every type the SUT touches must be registered first - viagivenor the registry.
Dispose discipline
The world owns subscriptions and internal mock-namespace.../waiter-namespace... registrators. Disposal is mandatory and must happen once per test:
dispose() resets the history, unsubscribes the mocks, tears down both internal namespaces, and disposes the bus. The world is not reusable afterwards; do not call dispose() twice and do not touch the world after it.
Low-level escape hatches
world.mocks
Use mocks when a constant result is not enough: dynamic answers, call counters, or throwing stubs.
world.registry
Direct registration on the world's namespace. In strict mode it mirrors the production registrator.
Note the difference: given.event(msg) registers a replay subject for the type and fires the instance immediately, so subscribers that attach later receive it through the buffer. registry.recordReplay(Type) only registers the subject - future subscribers get the next fired value, and nothing is sent now. Use the latter for "latest value on subscribe" semantics of a future event.
Pitfalls
Do not register or eliminate the internal
mock-namespace.../waiter-namespace...namespaces manually; that breaksdispose().world.mocks.dispose()exists but is rarely needed -world.dispose()already tears everything down.In strict mode a single missing registration makes the SUT throw at the first
fire/subof that type.The world constructor takes only
PostboyTestingSettings. Do not pass a history or a bus; the world builds its own.
Next steps
Assertions: the
thenbuilders and the history behind them.Async waiters: the
waitForfamily.Recipes: complete task-based tests, including a strict-mode one.
Registration: how registration works in production code.