claude plugin test. Una prueba genera los eventos que tus hooks manejan y verifica qué hicieron los hooks, para que detectes un problema antes de que llegue a una sesión. El primer ejemplo prueba el mod de Crear un mod.
Escribe una prueba
Una prueba carga tu mod, envía eventos a través de sus hooks de la manera que lo haría Claude Code, y verifica qué hicieron los hooks, sin una sesión, un inicio de sesión ni una red. Ejecutas las pruebas desde tu shell conclaude plugin test, y cada archivo de prueba importa el kit de pruebas, una biblioteca de pruebas en el módulo claude-code/testing.
Dale a cada archivo de prueba un nombre que termine en .test.ts, como first-mod.test.ts, y guárdalo en cualquier lugar del directorio del plugin. Cada archivo de prueba necesita al menos una test(), o la ejecución falla con declares no test(): nothing ran. Un archivo de prueba puede importar los propios archivos de tu mod y helpers .ts hermanos, para que puedas hacer pruebas unitarias de funciones simples, como las reglas de un juego, sin el kit.
Esta prueba genera dos llamadas de herramientas, ejecuta el comando /tally de Crear un mod, y verifica que la respuesta cuente ambas. Su primera línea es un stub, que responde las llamadas de herramientas en lugar de Claude Code. Guárdalo como first-mod/tests/first-mod.test.ts:
first-mod/tests/first-mod.test.ts
first-mod:
$.tool.call pasó a través del hook tool.call del mod, que agregó uno a su contador y pasó la llamada al stub. No se ejecutó ls y no se leyó ningún archivo. $.command.run luego fue al hook command.run del mod, y answer es el objeto que ese hook devolvió.
El comando sale con estado 1 cuando una prueba falla, por lo que funciona en CI. Si tus propios mods no pueden cargarse en el shell que lo ejecuta, imprime una línea que comienza con claude plugin test: hooks modules are turned off con la razón, y sale con estado 1.
Simula lo que Claude Code respondería
Ningún modelo, almacén o herramienta se ejecuta en una prueba, por lo que dondequiera que tu mod espere que Claude Code responda, la prueba proporciona la respuesta con un stub. Una función de prueba recibe dos argumentos para eso:$: el$propio de la prueba, que se sitúa donde Claude Code lo hace. No es la API de mods que recibe un hook. Cada uno de sus métodos genera el evento del mismo nombre, lo envía a través de los hooks de tu mod, y se resuelve al resultado:$.tool.call({ tool: 'Bash', command: 'ls' })generatool.call.$.command.run,$.prompt.submit,$.session.start, y$.turn.completefuncionan de la misma manera, y$.classic.Stopy los otros métodos$.classicgeneran un evento de hook de configuración. Una prueba no puede generar directamente una llamada de API de mods comoui.close. Actívala a través de tu mod, por ejemplo presionando el botón que cierra el panel.on: llámalo para registrar stubs, que son hooks que responden en lugar de Claude Code. Nombra un stub para una llamada de API de mods sin el$., por lo que un stub registrado comostore.getresponde a$.store.getde tu mod. Cuando tu mod llama a$.model.completeo$.store.get, un stub proporciona la respuesta.
grader, y maneja un comando /grade que envía una oración a un modelo e informa si la respuesta comienza con PASS. El archivo contiene solo el hook bajo prueba, por lo que el mod también necesita un plugin.json y un hooks.json, como en Crear un mod. Para escribir /grade en una sesión, el mod también tiene que registrar el comando:
grader/hooks/register.js
grader/tests/grader.test.ts
reply del hook es el objeto bajo value, cuyo text comienza con PASS. Para verificar la otra rama, agrega una segunda prueba cuyo stub devuelva un text que comience con FAIL, y espera Try again.
Un stub para una llamada de API de mods devuelve un objeto con un campo value, que contiene lo que la llamada se resuelve en tu mod: { value: 7 } hace que $.store.get se resuelva a 7. Un stub para uno de los eventos de Claude Code, como turn.step o tool.call, devuelve el resultado propio de ese evento, como { result: 'ok' }. $.session.send y $.prompt.fill también toman el resultado del evento, como muestra la tabla. Busca qué devuelve un stub muestra qué forma toma cada nombre común. Dos errores significan que un stub es incorrecto o falta. La salida de una prueba fallida incluye un bloque encabezado the engine reported:, y cada error aparece allí:
returned neither { value } nor { deny }: un stub para una llamada de API de mods devolvió un valor simpleno implementation forseguido de un nombre: tu mod hizo esa llamada y ningún stub la responde
mock.clock(on) responde $.clock, mock.store(on, { count: 7 }) responde $.store desde un almacén que comienza con esas entradas, y mock.env(on, { CI: 'true' }) responde $.env.get desde esas variables. mock.clock devuelve un reloj simulado que tu prueba avanza, por lo que una prueba de un temporizador no espera. mock.store no devuelve nada, por lo que para verificar qué guardó tu mod, escribe los dos stubs store tú mismo como lo hace la prueba de dibujo.
Sigue las reglas del kit de pruebas
El kit de pruebas tiene algunas reglas propias, y romper una produce los errores que los nuevos autores de pruebas encuentran primero:-
Registra cada stub antes de la primera llamada de la prueba a
$. Llamar aondespués de eso lanza un error comoon("ui.render") after the test first called $. -
session.startno se ejecuta por sí solo. Cada prueba comienza con tu módulo recién cargado y ninguno de sus hooks llamado, por lo que las variables a nivel de módulo mantienen sus valores iniciales. Si un hook depende de lo quesession.startconfigura, genéralo primero:El segundo stub responde la llamada$.command.registerque hace un hooksession.startcomo el del tutorial. Sin él, esa llamada rechaza conno implementation for command.registery el kit omite tu hook, por lo que nada después de la llamada en el hook se ejecuta. La prueba no falla en ese punto. El hook omitido se enumera bajothe engine reported:solo si una verificación posterior falla. -
Un hook que devuelve
next(e)necesita un stub para responder. Cuando tu hookui.renderdevuelvenext(e), por ejemplo para no dibujar nada mientras Claude está inactivo, montarlo falla conno implementation for ui.render. Registra un stub que devuelva un elemento como datos simples:Con el stub registrado, el montaje tiene éxito, yui.find({ type: 'Text' })devuelve ese elemento siempre que tu hook devolviónext(e). -
Un stub para
turn.stepes un generador asincrónico, y la prueba lee el flujo hasta su fin para obtener el resultado:Cuando el bucle termina,resultes el objeto que el stub devolvió, después de que tu hookturn.stephaya tenido la oportunidad de cambiarlo. Aquíresult.answeres'ok'. -
Genera una llamada de herramienta con el nombre de la herramienta y los argumentos como campos, como
await $.tool.call({ tool: 'Bash', command: 'ls' }), y registra un stubtool.callque devuelva{ result }.
Busca qué devuelve un stub
Cada llamada de API de mods que tu mod hace en una prueba necesita un stub que responda en lugar de Claude Code, excepto las pocas que el kit responde por sí solo: llamadas$.ui.invalidate y $.state. Para llamadas $.clock, usa mock.clock(on), o $.clock.now() de tu mod falla con no implementation for clock.now.
Esta tabla enumera las que los mods usan más. La primera columna es la llamada que tu mod hace o el evento que pasa con next(e). La segunda es la función a pasar a on bajo ese nombre, por lo que la fila $.store.get se convierte en on('store.get', ($, e) => ({ value: saved.get(e.key) })). Un '...' en un stub marca texto para que lo completes:
expect tiene las aserciones toBe, toEqual, toMatch, toMatchObject, toContain, toBeDefined, toBeUndefined, y toThrow, y .not antes de cualquiera de ellas.
Prueba un temporizador
Un mod que ejecuta trabajo en un temporizador necesita un reloj que la prueba controle, para que la prueba pueda avanzar el tiempo en lugar de esperar.const clock = mock.clock(on) devuelve un reloj simulado que comienza en 0 y se mueve solo cuando tu prueba lo mueve. Para comenzar en otro momento, pásalo en milisegundos, como en mock.clock(on, { now: 5000 }). El reloj tiene estos métodos:
Este hook pertenece a un mod llamado
countdown, y maneja un comando /countdown que toma un número de segundos, inicia un temporizador $.clock.every de un segundo, y muestra un toast en cero. Como con grader, el archivo contiene solo el hook bajo prueba y no registra el comando:
countdown/hooks/register.js
/countdown 3 y mueve el reloj simulado, por lo que verifica tres segundos de comportamiento sin esperar tres segundos:
countdown/tests/countdown.test.ts
expect muestra que el toast no viene temprano, y el segundo muestra que viene una vez. Cada advance se resuelve después de que los temporizadores que vencieron hayan ejecutado, por lo que la verificación en la siguiente línea ve su efecto.
Prueba un dibujo
Una prueba puede dibujar uno de los sitios de renderizado de tu mod, luego presionar, escribir en, y encontrar los elementos que dibujó.$.ui.mount dibuja el sitio a través del hook ui.render de tu mod y devuelve un identificador con un método para cada uno de esos. Para cubrir varias aplicaciones en una prueba, establece surface en la aplicación para la que dibujar. Esta prueba abre el panel de Construye un panel con pestañas, cambia pestañas, presiona el botón, y verifica el contador en la terminal y la aplicación de escritorio:
hello-tabs/tests/hello-tabs.test.ts
claude plugin test desde el directorio hello-tabs. La prueba pasa cuando ambas aplicaciones dibujan la línea de contador y el mod ha guardado 2. El contador se transfiere de la primera aplicación a la segunda porque ambos montajes usan el mismo módulo cargado.
El identificador que $.ui.mount devuelve tiene estos métodos, que direccionan elementos por la key que les diste:
Cada método se resuelve después de que tu controlador haya terminado, por lo que puedes verificar el resultado en la siguiente línea. Establece
props en lo que Claude Code pasaría para ese sitio. La tabla de sitios de renderizado enumera los props de cada sitio, y los tipos para tu compilación tienen sus tipos.
Una prueba de dibujo verifica el árbol que devuelve tu hook y si es válido para esa aplicación. No verifica cómo la aplicación lo pinta, así que mira un nuevo diseño en una sesión real también.
Prueba un dibujo después de /clear
Cada prueba comienza con cada valor $.state en su predeterminado, que es cómo /clear los deja. Para probar qué hace tu mod a continuación, omite session.start, genera classic.SessionStart con source: 'clear', y verifica qué dibuja tu mod.
Esta prueba verifica el módulo de Carga un valor guardado nuevamente después de /clear. Agrégalo al archivo de Prueba un dibujo, donde PANE está definido. La primera prueba de ese archivo espera que el botón guarde el contador, como lo hace el botón en Guarda desde más de una sesión:
hello-tabs/tests/hello-tabs.test.ts
classic.SessionStart ha copiado el 7 guardado en $.state antes de que el panel se dibuje. Sin ese hook en tu módulo, el panel dibuja Count: 0, find devuelve undefined, y la prueba falla en toBeDefined.
Prueba un mod que juzga otros mods
Un mod que tu organización enumera enprependPlugins puede rechazar otro mod antes de que cargue. Para probar uno, establece el nivel de tu mod y dale a la prueba un segundo mod para que el tuyo admita o rechace:
tier: llámalo una vez en la parte superior del archivo de prueba, como entier('prepend'), para cargar tu mod comoprepend,append, obuiltin, su lugar en el orden en que los mods se ejecutan. Sin él, tu mod carga comouser.plugins: pasa atestun objeto de opciones antes del cuerpo de la prueba. Su matrizpluginscontiene mods que escribes en línea, cada uno con unnamey una funciónregister. Para cargar uno en algún lugar que no seauser, agregatiera él.
acme-guard/tests/guard.test.ts
claude plugin test desde el directorio acme-guard. Ambas pruebas pasan con el mod de política como lo muestra la página de administración.
El kit carga cada mod en la primera llamada de la prueba a $. Cuando tu mod rechaza uno, esa llamada lanza, y el mensaje nombra el mod rechazado, el mod que lo rechazó, y tu razón. En la segunda prueba nada es rechazado, por lo que reader responde la llamada de herramienta antes de que llegue al stub.
Próximos pasos
- Soluciona problemas de un mod: descubre por qué un mod no hace nada en una sesión
- Referencia de mods: cada evento de entrada y resultado, para escribir stubs