TestProbe
Ein TestProbe ist eine ActorRef, die keinen User-Code
ausführt. Stattdessen landet jedes tell in einem Buffer, auf
den der Test asserten kann. Übergib eine Probe überall, wo du
einen echten Actor übergeben würdest; der zu testende Actor
sieht eine normale Ref und tellt sie wie üblich.
const tk = TestKit.create();const probe = tk.createTestProbe();
const ref = tk.system.spawnAnonymous(Props.create(() => new Worker(probe)));// ^-- Probe anstelle des echten Downstreamsref.tell({ kind: 'work' });
await probe.expectMsg({ kind: 'work-done' });await tk.shutdown();Worker tellt den „Downstream” — der tatsächlich die Probe
ist. Die Probe erfasst die Nachricht; expectMsg konsumiert sie
und assertet die Form.
Eine Probe erzeugen
Abschnitt betitelt „Eine Probe erzeugen“const probe = tk.createTestProbe({ defaultTimeoutMs: 5_000, // Default-Wartezeit für expect/receive — Default 3s name: 'order-probe',});Beide Optionen sind optional. Ohne defaultTimeoutMs wartet
jeder expect*-Aufruf bis zu 3 Sekunden. Ohne name bekommt
die Probe einen auto-generierten Pfad.
Die Probe erbt von ActorRef, sodass du sie überall übergeben
kannst, wo eine ActorRef<T> erwartet wird — obwohl sie als
unknown typisiert ist (sie akzeptiert jede Nachrichten-Art).
Caste, falls nötig:
ref.tell({ kind: 'get', replyTo: probe as ActorRef<number> });Erwartungen
Abschnitt betitelt „Erwartungen“expectMsg(expected)
Abschnitt betitelt „expectMsg(expected)“await probe.expectMsg('done');await probe.expectMsg({ kind: 'reply', value: 42 });Wartet auf die nächste Nachricht, vergleicht sie deep-equal mit
expected. Wirft, wenn die Form nicht passt oder das Timeout
abläuft. Gibt die Nachricht zurück, sodass du Assertions ketten
kannst.
expectMsgType(Class)
Abschnitt betitelt „expectMsgType(Class)“import { Success } from 'actor-ts';
const reply = await probe.expectMsgType(Success);expect(reply.value).toBe(42);Wartet auf die nächste Nachricht; assertet, dass sie eine
Instanz von Class ist. Nützlich für Success- / Failure-
Envelopes aus pipeTo oder
beliebige klassenbasierte Message-Typen.
expectNoMessage(timeoutMs)
Abschnitt betitelt „expectNoMessage(timeoutMs)“ref.tell({ kind: 'do-nothing' });await probe.expectNoMessage(200);// → assertet, dass in 200ms keine Nachricht kamDas Gegenteil von expectMsg — assertet, dass im gegebenen
Fenster nichts ankommt. Nützlich für „dieses Kommando sollte
still ignoriert werden” oder „der Actor sollte darauf nicht
antworten.”
Das Default-Fenster sind 300 ms — kurz genug, um Tests schnell zu halten, lang genug, um die meisten unerwünschten Sends zu fangen.
receiveOne(timeoutMs?) / receiveN(n, timeoutMs?)
Abschnitt betitelt „receiveOne(timeoutMs?) / receiveN(n, timeoutMs?)“const msg = await probe.receiveOne();const msgs = await probe.receiveN(3);Hole die nächste(n) Nachricht(en), ohne zu asserten. Nutze es, wenn die Form variiert und du sie mit eigener Logik inspizieren willst:
const msg = await probe.receiveOne();if (msg.kind === 'success') expect(msg.value).toBeGreaterThan(0);else expect(msg.error).toBeDefined();fishForMessage(predicate)
Abschnitt betitelt „fishForMessage(predicate)“const ready = await probe.fishForMessage( (m) => typeof m === 'object' && m.kind === 'ready', 5_000,);Verwirf Nachrichten, bis eine das Prädikat erfüllt. Nützlich, wenn der zu testende Actor einen Strom von Nachrichten emittiert und dich nur eine bestimmte Art interessiert. Verworfene Nachrichten sind weg — sie können später nicht mehr assertet werden.
messageCount / hasMessage()
Abschnitt betitelt „messageCount / hasMessage()“expect(probe.messageCount).toBe(0);ref.tell({ kind: 'do' });await new Promise(r => setImmediate(r)); // einmal yielden, damit der tell ankommtexpect(probe.hasMessage()).toBe(true);Synchrone Inspektion — kein Warten. Nutze, um zu prüfen „gibt es gequeuete Nachrichten, die ich noch nicht konsumiert habe?”
Der letzte Sender
Abschnitt betitelt „Der letzte Sender“ref.tell({ kind: 'get', replyTo: probe });await probe.expectMsg({ kind: 'reply', value: 42 });expect(probe.sender).toBe(ref); // wer die zuletzt empfangene Nachricht gesendet hatNach expectMsg / receiveOne / etc. hält probe.sender den
Sender der eben konsumierten Nachricht. Nützlich, um zu
asserten, dass „diese Antwort vom richtigen Actor kam.”
probe.reply(msg) ist eine Abkürzung, die dem letzten Sender
tellt:
ref.tell({ kind: 'request' }, probe);await probe.expectMsg({ kind: 'request' });probe.reply({ kind: 'response' });// ↑ dasselbe wie: probe.sender.tell({ kind: 'response' }, probe);Gängig in Tests, die Zwei-Wege-Konversationen simulieren — lass die Probe einen Request empfangen, dann antworten lassen.
clearInbox
Abschnitt betitelt „clearInbox“ref.tell({ kind: 'one' });ref.tell({ kind: 'two' });probe.clearInbox(); // verwirft beide, Queue ist wieder leer
ref.tell({ kind: 'three' });await probe.expectMsg({ kind: 'three' });Nützlich zwischen Testphasen, wenn du Setup-Phasen-Nachrichten ignorieren willst und nur auf das asserten möchtest, was als Nächstes kommt.
Probe als Stellvertreter für einen Downstream-Actor
Abschnitt betitelt „Probe als Stellvertreter für einen Downstream-Actor“class OrderHandler extends Actor<OrderCmd> { constructor(private readonly db: ActorRef<DbCmd>) { super(); } override onReceive(cmd: OrderCmd): void { this.db.tell({ kind: 'insert', orderId: cmd.id }); }}
it('leitet Orders an db weiter', async () => { const dbProbe = tk.createTestProbe(); const handler = tk.system.spawnAnonymous(Props.create(() => new OrderHandler(dbProbe as ActorRef<DbCmd>)));
handler.tell({ kind: 'place-order', id: 'o-1' }); await dbProbe.expectMsg({ kind: 'insert', orderId: 'o-1' });});Die Probe ersetzt einen echten DB-Actor; der Test sieht den Call, ohne dass die DB existieren muss.
Probe als Reply-to-Ziel
Abschnitt betitelt „Probe als Reply-to-Ziel“const counter = tk.system.spawnAnonymous(Props.create(() => new Counter()));counter.tell({ kind: 'inc' });counter.tell({ kind: 'inc' });counter.tell({ kind: 'get', replyTo: probe as ActorRef<number> });await probe.expectMsg(2);Für Ask-artige Nachrichten — die Probe steht für den Fragenden.
Mehrere Proben
Abschnitt betitelt „Mehrere Proben“const a = tk.createTestProbe({ name: 'a-probe' });const b = tk.createTestProbe({ name: 'b-probe' });
const router = tk.system.spawnAnonymous(...);router.tell({ kind: 'send', target: 'a', payload: 'hello' });
await a.expectMsg('hello');await b.expectNoMessage(100);Routing-Logik verifizieren, indem du den Inbox jeder Probe prüfst.
Stolperfallen
Abschnitt betitelt „Stolperfallen“Wo es weitergeht
Abschnitt betitelt „Wo es weitergeht“- TestKit — der Wrapper, der die Probe erzeugt.
- Testing — Überblick — wann Proben vs andere Test-Werkzeuge nutzen.
- ManualScheduler — für Tests, die deterministisches Timer-Feuern brauchen.
Die TestProbe-API-Referenz deckt
die volle Oberfläche ab.