claude plugin test ausführen. Ein Test löst die Ereignisse aus, die Ihre Hooks verarbeiten, und überprüft, was die Hooks getan haben, damit Sie ein Problem fangen, bevor es eine Sitzung erreicht. Das erste Beispiel testet den Mod aus Einen Mod erstellen.
Schreiben Sie einen Test
Ein Test lädt Ihr Mod, sendet Ereignisse durch seine Hooks so, wie Claude Code es würde, und prüft, was die Hooks getan haben, ohne eine Sitzung, eine Anmeldung oder ein Netzwerk. Sie führen Tests aus Ihrer Shell mitclaude plugin test aus, und jede Testdatei importiert das Test-Kit, eine Test-Bibliothek im Modul claude-code/testing.
Geben Sie jeder Testdatei einen Namen, der auf .test.ts endet, z. B. first-mod.test.ts, und speichern Sie sie überall im Plugin-Verzeichnis. Jede Testdatei benötigt mindestens einen test(), sonst schlägt die Ausführung mit declares no test(): nothing ran fehl. Eine Testdatei kann die eigenen Dateien Ihres Mods und Hilfsdateien mit .ts importieren, sodass Sie einfache Funktionen, wie die Regeln eines Spiels, ohne das Kit testen können.
Dieser Test löst zwei Tool-Aufrufe aus, führt den Befehl /tally aus Create a mod aus und prüft, dass die Antwort beide zählt. Seine erste Zeile ist ein Stub, der die Tool-Aufrufe an Stelle von Claude Code beantwortet. Speichern Sie ihn als first-mod/tests/first-mod.test.ts:
first-mod/tests/first-mod.test.ts
first-mod aus:
$.tool.call durchlief den tool.call-Hook des Mods, der eins zu seiner Zählung addierte und den Aufruf an den Stub weitergab. Kein ls wurde ausgeführt und keine Datei wurde gelesen. $.command.run ging dann zum command.run-Hook des Mods, und answer ist das Objekt, das dieser Hook zurückgab.
Der Befehl wird mit Status 1 beendet, wenn ein Test fehlschlägt, sodass er in CI funktioniert. Wenn Ihre eigenen Mods nicht in der Shell geladen werden können, die ihn ausführt, druckt er eine Zeile aus, die mit claude plugin test: hooks modules are turned off beginnt, mit dem Grund, und wird mit Status 1 beendet.
Stub was Claude Code antworten würde
Kein Modell, Speicher oder Tool wird in einem Test ausgeführt, daher liefert der Test überall dort, wo Ihr Mod erwartet, dass Claude Code antwortet, die Antwort mit einem Stub. Eine Test-Funktion erhält zwei Argumente dafür:$: das eigene$des Tests, das an der Stelle von Claude Code steht. Es ist nicht die Mods-API, die ein Hook erhält. Jede seiner Methoden löst das Ereignis desselben Namens aus, sendet es durch die Hooks Ihres Mods und wird zum Ergebnis aufgelöst:$.tool.call({ tool: 'Bash', command: 'ls' })lösttool.callaus.$.command.run,$.prompt.submit,$.session.startund$.turn.completefunktionieren auf die gleiche Weise, und$.classic.Stopund die anderen$.classic-Methoden lösen ein Einstellungs-Hook-Ereignis aus. Ein Test kann einen Mods-API-Aufruf wieui.closenicht direkt auslösen. Lösen Sie ihn durch Ihr Mod aus, z. B. indem Sie auf die Schaltfläche klicken, die den Bereich schließt.on: Rufen Sie es auf, um Stubs zu registrieren, die Hooks sind, die an Stelle von Claude Code antworten. Benennen Sie einen Stub für einen Mods-API-Aufruf ohne das$., sodass ein alsstore.getregistrierter Stub die$.store.getIhres Mods beantwortet. Wenn Ihr Mod$.model.completeoder$.store.getaufruft, liefert ein Stub die Antwort.
grader und verarbeitet einen /grade-Befehl, der einen Satz an ein Modell sendet und meldet, ob die Antwort mit PASS beginnt. Die Datei enthält nur den Hook unter Test, daher benötigt das Mod auch eine plugin.json und eine hooks.json, wie in Create a mod. Um /grade in einer Sitzung einzugeben, muss das Mod auch den Befehl registrieren:
grader/hooks/register.js
grader/tests/grader.test.ts
reply des Hooks das Objekt unter value ist, dessen text mit PASS beginnt. Um den anderen Zweig zu prüfen, fügen Sie einen zweiten Test hinzu, dessen Stub einen text zurückgibt, der mit FAIL beginnt, und erwarten Sie Try again.
Ein Stub für einen Mods-API-Aufruf gibt ein Objekt mit einem value-Feld zurück, das hält, was der Aufruf in Ihrem Mod aufgelöst wird: { value: 7 } macht $.store.get zu 7 aufgelöst. Ein Stub für eines der Ereignisse von Claude Code, wie turn.step oder tool.call, gibt das eigene Ergebnis dieses Ereignisses zurück, wie { result: 'ok' }. $.session.send und $.prompt.fill nehmen auch das Ergebnis des Ereignisses, wie die Tabelle zeigt. Nachschlagen, was ein Stub zurückgibt zeigt, welche Form jeder häufige Name annimmt. Zwei Fehler bedeuten, dass ein Stub falsch oder fehlend ist. Die Ausgabe eines fehlgeschlagenen Tests enthält einen Block mit der Überschrift the engine reported:, und jeder Fehler erscheint dort:
returned neither { value } nor { deny }: ein Stub für einen Mods-API-Aufruf gab einen bloßen Wert zurückno implementation forgefolgt von einem Namen: Ihr Mod hat diesen Aufruf gemacht und kein Stub beantwortet ihn
mock.clock(on) beantwortet $.clock, mock.store(on, { count: 7 }) beantwortet $.store aus einem Speicher, der mit diesen Einträgen beginnt, und mock.env(on, { CI: 'true' }) beantwortet $.env.get aus diesen Variablen. mock.clock gibt eine Mock-Uhr zurück, die Ihr Test vorantreibt, sodass ein Test eines Timers nicht wartet. mock.store gibt nichts zurück, daher schreiben Sie die beiden store-Stubs selbst, wie der Zeichnungstest es tut.
Befolgen Sie die Regeln des Test-Kits
Das Test-Kit hat ein paar eigene Regeln, und das Brechen einer produziert die Fehler, die neue Test-Autoren zuerst treffen:-
Registrieren Sie jeden Stub vor dem ersten Aufruf des Tests auf
$. Das Aufrufen vonondanach wirft einen Fehler wieon("ui.render") after the test first called $. -
session.startwird nicht von selbst ausgeführt. Jeder Test beginnt mit Ihrem Modul frisch geladen und keiner seiner Hooks aufgerufen, daher halten Variablen auf Modulebene ihre Anfangswerte. Wenn ein Hook davon abhängt, wassession.starteinrichtet, lösen Sie es zuerst aus:Der zweite Stub beantwortet den$.command.register-Aufruf, den einsession.start-Hook wie der Tutorial macht. Ohne ihn wird dieser Aufruf mitno implementation for command.registerabgelehnt und das Kit überspringt Ihren Hook, sodass nichts nach dem Aufruf im Hook ausgeführt wird. Der Test schlägt an diesem Punkt nicht fehl. Der übersprungene Hook wird unterthe engine reported:nur aufgelistet, wenn eine spätere Prüfung fehlschlägt. -
Ein Hook, der
next(e)zurückgibt, benötigt einen Stub zum Beantworten. Wenn Ihrui.render-Hooknext(e)zurückgibt, z. B. um nichts zu zeichnen, während Claude untätig ist, schlägt das Mounten mitno implementation for ui.renderfehl. Registrieren Sie einen Stub, der ein Element als einfache Daten zurückgibt:Mit dem registrierten Stub wird das Mounten erfolgreich, undui.find({ type: 'Text' })gibt dieses Element zurück, wann immer Ihr Hooknext(e)zurückgab. -
Ein Stub für
turn.stepist ein asynchroner Generator, und der Test liest den Stream bis zum Ende, um das Ergebnis zu erhalten:Wenn die Schleife endet, istresultdas Objekt, das der Stub zurückgab, nachdem Ihrturn.step-Hook die Chance hatte, es zu ändern. Hier istresult.answer'ok'. -
Lösen Sie einen Tool-Aufruf mit dem Namen und den Argumenten des Tools als Felder aus, wie
await $.tool.call({ tool: 'Bash', command: 'ls' }), und registrieren Sie einentool.call-Stub, der{ result }zurückgibt.
Nachschlagen, was ein Stub zurückgibt
Jeder Mods-API-Aufruf, den Ihr Mod in einem Test macht, benötigt einen Stub, der an Stelle von Claude Code antwortet, außer den wenigen, die das Kit selbst beantwortet:$.ui.invalidate und $.state-Aufrufe. Für $.clock-Aufrufe verwenden Sie mock.clock(on), oder die $.clock.now() Ihres Mods schlägt mit no implementation for clock.now fehl.
Diese Tabelle listet die auf, die Mods am häufigsten verwenden. Die erste Spalte ist der Aufruf, den Ihr Mod macht, oder das Ereignis, das es mit next(e) weitergegeben wird. Die zweite ist die Funktion, die unter diesem Namen an on übergeben wird, sodass die Zeile $.store.get zu on('store.get', ($, e) => ({ value: saved.get(e.key) })) wird. Ein '...' in einem Stub markiert Text, den Sie ausfüllen müssen:
expect hat die Assertions toBe, toEqual, toMatch, toMatchObject, toContain, toBeDefined, toBeUndefined und toThrow, und .not vor jedem von ihnen.
Einen Timer testen
Ein Mod, der Arbeit auf einem Timer ausführt, benötigt eine Uhr, die der Test steuert, sodass der Test die Zeit vorantreiben kann, anstatt zu warten.const clock = mock.clock(on) gibt eine Mock-Uhr zurück, die bei 0 beginnt und sich nur bewegt, wenn Ihr Test sie bewegt. Um bei einer anderen Zeit zu beginnen, übergeben Sie sie in Millisekunden, wie in mock.clock(on, { now: 5000 }). Die Uhr hat diese Methoden:
Dieser Hook gehört zu einem Mod namens
countdown und verarbeitet einen Befehl /countdown, der eine Anzahl von Sekunden nimmt, einen $.clock.every-Timer von einer Sekunde startet und einen Toast bei Null zeigt. Wie bei grader enthält die Datei nur den Hook unter Test und registriert den Befehl nicht:
countdown/hooks/register.js
/countdown 3 aus und bewegt die Mock-Uhr, sodass er drei Sekunden Verhalten überprüft, ohne drei Sekunden zu warten:
countdown/tests/countdown.test.ts
expect zeigt, dass der Toast nicht früh kommt, und das zweite zeigt, dass er einmal kommt. Jeder advance wird aufgelöst, nachdem die Timer, die fällig wurden, ausgeführt wurden, sodass die Überprüfung auf der nächsten Zeile ihre Auswirkung sieht.
Eine Zeichnung testen
Ein Test kann eine der Render-Stellen Ihres Mods zeichnen, dann Elemente drücken, eingeben und finden, die er gezeichnet hat.$.ui.mount zeichnet die Stelle durch den Hook ui.render Ihres Mods und gibt ein Handle mit einer Methode für jeden zurück. Um mehrere Apps in einem Test abzudecken, setzen Sie surface auf die App, für die gezeichnet werden soll. Dieser Test öffnet den Bereich aus Einen Bereich mit Registerkarten erstellen, wechselt Registerkarten, drückt die Schaltfläche und überprüft die Zählung im Terminal und der Desktop-App:
hello-tabs/tests/hello-tabs.test.ts
claude plugin test aus dem Verzeichnis hello-tabs aus. Der Test besteht, wenn beide Apps die Zählzeile zeichnen und der Mod 2 gespeichert hat. Die Zählung wird von der ersten App zur zweiten übertragen, weil beide Mounts das gleiche geladene Modul verwenden.
Das Handle, das $.ui.mount zurückgibt, hat diese Methoden, die Elemente nach dem key adressieren, den Sie ihnen gegeben haben:
Jede Methode wird aufgelöst, nachdem Ihr Handler fertig ist, sodass Sie das Ergebnis auf der nächsten Zeile überprüfen können. Setzen Sie
props auf das, was Claude Code für diese Stelle übergeben würde. Die Render-Stellen-Tabelle listet die Props jeder Stelle auf, und die Typen für Ihren Build haben ihre Typen.
Ein Zeichnungstest überprüft den Baum, den Ihr Hook zurückgibt, und ob er für diese App gültig ist. Er überprüft nicht, wie die App ihn malt, daher schauen Sie sich ein neues Layout auch in einer echten Sitzung an.
Eine Zeichnung nach /clear testen
Jeder Test beginnt mit jedem $.state-Wert bei seinem Standard, was ist, wie /clear sie hinterlässt. Um zu testen, was Ihr Mod als nächstes tut, überspringen Sie session.start, lösen Sie classic.SessionStart mit source: 'clear' aus und überprüfen Sie, was Ihr Mod zeichnet.
Dieser Test überprüft das Modul aus Einen gespeicherten Wert nach /clear erneut laden. Fügen Sie es zur Datei aus Eine Zeichnung testen hinzu, wo PANE definiert ist. Der erste Test dieser Datei erwartet, dass die Schaltfläche die Zählung speichert, wie die Schaltfläche in Von mehr als einer Sitzung speichern es tut:
hello-tabs/tests/hello-tabs.test.ts
classic.SessionStart-Hook die gespeicherte 7 in $.state kopiert hat, bevor der Bereich gezeichnet wird. Ohne diesen Hook in Ihrem Modul zeichnet der Bereich Count: 0, find gibt undefined zurück, und der Test schlägt bei toBeDefined fehl.
Einen Mod testen, der andere Mods beurteilt
Ein Mod, den Ihre Organisation inprependPlugins auflistet, kann einen anderen Mod ablehnen, bevor er geladen wird. Um einen zu testen, setzen Sie den Tier Ihres Mods und geben Sie dem Test einen zweiten Mod, den Ihrer zulassen oder ablehnen kann:
tier: rufen Sie es einmal oben in der Testdatei auf, wie intier('prepend'), um Ihren Mod alsprepend,appendoderbuiltinzu laden, seinen Platz in der Reihenfolge, in der Mods ausgeführt werden. Ohne ihn wird Ihr Mod alsusergeladen.plugins: übergeben Sietestein Optionsobjekt vor dem Test-Body. Seinplugins-Array hält Mods, die Sie inline schreiben, jeder mit einemnameund einerregister-Funktion. Um einen an einer anderen Stelle alsuserzu laden, fügen Sietierhinzu.
acme-guard/tests/guard.test.ts
claude plugin test aus dem Verzeichnis acme-guard aus. Beide Tests bestehen mit dem Policy-Mod, wie die Admin-Seite ihn zeigt.
Das Kit lädt jeden Mod beim ersten Aufruf des Tests auf $. Wenn Ihr Mod einen ablehnt, wirft dieser Aufruf, und die Nachricht nennt den abgelehnten Mod, den Mod, der ihn ablehnt, und Ihren Grund. Im zweiten Test wird nichts abgelehnt, daher beantwortet reader den Tool-Aufruf, bevor er den Stub erreicht.
Nächste Schritte
- Einen Mod beheben: finden Sie heraus, warum ein Mod in einer Sitzung nichts tut
- Mods-Referenz: jedes Ereignis-Input und Ergebnis zum Schreiben von Stubs