Skip to main content
Anda dapat menulis tes otomatis untuk mod dan menjalankannya dari shell Anda dengan claude plugin test. Tes menaikkan peristiwa yang ditangani hook Anda dan memeriksa apa yang dilakukan hook, sehingga Anda menangkap masalah sebelum mencapai sesi. Contoh pertama menguji mod dari Buat mod.

Tulis tes

Tes memuat mod Anda, mengirim peristiwa melalui hook dengan cara Claude Code akan melakukannya, dan memeriksa apa yang dilakukan hook, tanpa sesi, masuk, atau jaringan. Anda menjalankan tes dari shell Anda dengan claude plugin test, dan setiap file tes mengimpor test kit, perpustakaan tes dalam modul claude-code/testing. Berikan setiap file tes nama yang diakhiri dengan .test.ts, seperti first-mod.test.ts, dan simpan di mana saja dalam direktori plugin. Setiap file tes memerlukan setidaknya satu test(), atau jalankan gagal dengan declares no test(): nothing ran. File tes dapat mengimpor file mod Anda sendiri dan pembantu .ts saudara, sehingga Anda dapat menguji unit fungsi biasa, seperti aturan permainan, tanpa kit. Tes ini menaikkan dua panggilan alat, menjalankan perintah /tally dari Buat mod, dan memeriksa bahwa balasan menghitung keduanya. Baris pertamanya adalah stub, yang menjawab panggilan alat di tempat Claude Code. Simpan sebagai first-mod/tests/first-mod.test.ts:
first-mod/tests/first-mod.test.ts
Di shell Anda, jalankan tes dari direktori first-mod:
Output menamai setiap tes dan apakah itu lulus, dengan waktu yang bervariasi dari jalankan ke jalankan:
Setiap $.tool.call melalui hook tool.call mod, yang menambah satu ke hitungannya dan meneruskan panggilan ke stub. Tidak ada ls yang berjalan dan tidak ada file yang dibaca. $.command.run kemudian pergi ke hook command.run mod, dan answer adalah objek yang dikembalikan hook. Perintah keluar dengan status 1 ketika tes gagal, sehingga berfungsi di CI. Jika mod Anda sendiri tidak dapat dimuat di shell yang menjalankannya, itu mencetak baris yang dimulai dengan claude plugin test: hooks modules are turned off dengan alasannya, dan keluar dengan status 1.

Stub apa yang akan dijawab Claude Code

Tidak ada model, toko, atau alat yang berjalan dalam tes, jadi di mana pun mod Anda mengharapkan Claude Code untuk menjawab, tes menyediakan jawaban dengan stub. Fungsi tes menerima dua argumen untuk itu:
  • $: $ tes sendiri, yang berdiri di mana Claude Code berada. Ini bukan mods API yang diterima hook. Setiap metodenya menaikkan peristiwa dengan nama yang sama, mengirimnya melalui hook mod Anda, dan menyelesaikan ke hasil: $.tool.call({ tool: 'Bash', command: 'ls' }) menaikkan tool.call. $.command.run, $.prompt.submit, $.session.start, dan $.turn.complete bekerja dengan cara yang sama, dan $.classic.Stop dan metode $.classic lainnya menaikkan peristiwa hook pengaturan. Tes tidak dapat menaikkan panggilan mods API seperti ui.close secara langsung. Picu melalui mod Anda, misalnya dengan menekan tombol yang menutup panel.
  • on: panggil untuk mendaftarkan stub, yang merupakan hook yang menjawab di tempat Claude Code. Beri nama stub untuk panggilan mods API tanpa $., jadi stub yang didaftarkan sebagai store.get menjawab $.store.get mod Anda. Ketika mod Anda memanggil $.model.complete atau $.store.get, stub menyediakan jawaban.
Contoh ini stub panggilan model. Hook milik mod bernama grader, dan menangani perintah /grade yang mengirim kalimat ke model dan melaporkan apakah balasan dimulai dengan PASS. File hanya menyimpan hook yang diuji, jadi mod juga memerlukan plugin.json dan hooks.json, seperti dalam Buat mod. Untuk mengetik /grade dalam sesi, mod juga harus mendaftarkan perintah:
grader/hooks/register.js
Tes ini stub panggilan model untuk memeriksa apa yang dilakukan hook dengan balasan yang lulus:
grader/tests/grader.test.ts
Tes lulus karena reply hook adalah objek di bawah value, yang text dimulai dengan PASS. Untuk memeriksa cabang lain, tambahkan tes kedua yang stub mengembalikan text yang dimulai dengan FAIL, dan harapkan Try again. Stub untuk panggilan mods API mengembalikan objek dengan bidang value, yang menyimpan apa yang dipanggil dalam mod Anda: { value: 7 } membuat $.store.get menyelesaikan ke 7. Stub untuk salah satu peristiwa Claude Code, seperti turn.step atau tool.call, mengembalikan hasil peristiwa itu sendiri, seperti { result: 'ok' }. $.session.send dan $.prompt.fill mengambil hasil peristiwa juga, seperti yang ditunjukkan tabel. Lihat apa yang dikembalikan stub menunjukkan bentuk mana yang diambil setiap nama umum. Dua kesalahan berarti stub salah atau hilang. Output tes yang gagal mencakup blok yang dikepalai the engine reported:, dan setiap kesalahan muncul di sana:
  • returned neither { value } nor { deny }: stub untuk panggilan mods API mengembalikan nilai telanjang
  • no implementation for diikuti oleh nama: mod Anda membuat panggilan itu dan tidak ada stub yang menjawabnya
Kit juga mengekspor mock dalam memori yang menjawab seluruh namespace untuk Anda. mock.clock(on) menjawab $.clock, mock.store(on, { count: 7 }) menjawab $.store dari toko yang dimulai dengan entri tersebut, dan mock.env(on, { CI: 'true' }) menjawab $.env.get dari variabel tersebut. mock.clock mengembalikan jam mock yang tes Anda maju, sehingga tes timer tidak menunggu. mock.store mengembalikan tidak ada, jadi untuk memeriksa apa yang disimpan mod Anda, tulis dua stub store sendiri seperti tes gambar lakukan.

Ikuti aturan test kit

Test kit memiliki beberapa aturan sendiri, dan melanggar satu menghasilkan kesalahan yang ditemui penulis tes baru terlebih dahulu:
  • Daftarkan setiap stub sebelum panggilan pertama tes pada $. Memanggil on setelah itu melempar kesalahan seperti on("ui.render") after the test first called $.
  • session.start tidak berjalan dengan sendirinya. Setiap tes dimulai dengan modul Anda dimuat segar dan tidak ada hook yang dipanggil, jadi variabel tingkat modul menyimpan nilai awal mereka. Jika hook bergantung pada apa yang ditetapkan session.start, naikkan terlebih dahulu:
    Stub kedua menjawab panggilan $.command.register yang dibuat hook session.start seperti tutorial. Tanpa itu, panggilan itu menolak dengan no implementation for command.register dan kit melewati hook Anda, jadi tidak ada yang setelah panggilan dalam hook yang berjalan. Tes tidak gagal pada titik itu. Hook yang dilewati hanya tercantum di bawah the engine reported: jika pemeriksaan nanti gagal.
  • Hook yang mengembalikan next(e) memerlukan stub untuk menjawab. Ketika hook ui.render Anda mengembalikan next(e), misalnya untuk tidak menggambar apa pun saat Claude menganggur, memasangnya gagal dengan no implementation for ui.render. Daftarkan stub yang mengembalikan elemen sebagai data biasa:
    Dengan stub terdaftar, pemasangan berhasil, dan ui.find({ type: 'Text' }) mengembalikan elemen itu setiap kali hook Anda mengembalikan next(e).
  • Stub untuk turn.step adalah generator async, dan tes membaca aliran ke akhirnya untuk mendapatkan hasil:
    Ketika loop berakhir, result adalah objek yang dikembalikan stub, setelah hook turn.step Anda memiliki kesempatan untuk mengubahnya. Di sini result.answer adalah 'ok'.
  • Naikkan panggilan alat dengan nama alat dan argumen sebagai bidang, seperti await $.tool.call({ tool: 'Bash', command: 'ls' }), dan daftarkan stub tool.call yang mengembalikan { result }.

Lihat apa yang dikembalikan stub

Setiap panggilan mods API yang dibuat mod Anda dalam tes memerlukan stub yang menjawab di tempat Claude Code, kecuali beberapa yang dijawab kit sendiri: panggilan $.ui.invalidate dan $.state. Untuk panggilan $.clock, gunakan mock.clock(on), atau $.clock.now() mod Anda gagal dengan no implementation for clock.now. Tabel ini mencantumkan yang paling sering digunakan mod. Kolom pertama adalah panggilan yang dibuat mod Anda atau peristiwa yang dilewatkan dengan next(e). Kolom kedua adalah fungsi untuk diteruskan ke on di bawah nama itu, jadi baris $.store.get menjadi on('store.get', ($, e) => ({ value: saved.get(e.key) })). '...' dalam stub menandai teks untuk Anda isi: expect memiliki asersi toBe, toEqual, toMatch, toMatchObject, toContain, toBeDefined, toBeUndefined, dan toThrow, dan .not sebelum salah satu dari mereka.

Uji coba timer

Mod yang menjalankan pekerjaan pada timer memerlukan jam yang dikontrol tes, sehingga tes dapat memajukan waktu alih-alih menunggu. const clock = mock.clock(on) mengembalikan jam mock yang dimulai pada 0 dan bergerak hanya ketika tes Anda memindahkannya. Untuk memulai pada waktu lain, teruskan dalam milidetik, seperti dalam mock.clock(on, { now: 5000 }). Jam memiliki metode ini: Hook ini milik mod bernama countdown, dan menangani perintah /countdown yang mengambil jumlah detik, memulai timer $.clock.every satu detik, dan menunjukkan toast pada nol. Seperti dengan grader, file hanya menyimpan hook yang diuji dan tidak mendaftarkan perintah:
countdown/hooks/register.js
Tes ini menjalankan /countdown 3 dan memajukan jam mock, sehingga memeriksa tiga detik perilaku tanpa menunggu tiga detik:
countdown/tests/countdown.test.ts
expect pertama menunjukkan bahwa toast tidak datang lebih awal, dan yang kedua menunjukkan bahwa itu datang sekali. Setiap advance menyelesaikan setelah timer yang jatuh tempo telah berjalan, sehingga pemeriksaan pada baris berikutnya melihat efeknya.

Uji coba gambar

Tes dapat menggambar salah satu situs render mod Anda, kemudian menekan, mengetik ke, dan menemukan elemen yang digambarnya. $.ui.mount menggambar situs melalui hook ui.render mod Anda dan mengembalikan handle dengan metode untuk masing-masing. Untuk mencakup beberapa aplikasi dalam satu tes, atur surface ke aplikasi untuk digambar. Tes ini membuka panel dari Bangun panel dengan tab, beralih tab, menekan tombol, dan memeriksa hitungan di terminal dan aplikasi Desktop:
hello-tabs/tests/hello-tabs.test.ts
Di shell Anda, jalankan claude plugin test dari direktori hello-tabs. Tes lulus ketika kedua aplikasi menggambar garis hitungan dan mod telah menyimpan 2. Hitungan dibawa dari aplikasi pertama ke yang kedua karena kedua pemasangan menggunakan modul yang dimuat sama. Handle yang dikembalikan $.ui.mount memiliki metode ini, yang mengatasi elemen berdasarkan key yang Anda berikan: Setiap metode menyelesaikan setelah handler Anda selesai, sehingga Anda dapat memeriksa hasil pada baris berikutnya. Atur props ke apa yang akan dilewatkan Claude Code untuk situs itu. Tabel situs render mencantumkan props setiap situs, dan tipe untuk build Anda memiliki tipe mereka. Tes gambar memeriksa pohon yang dikembalikan hook Anda dan apakah itu valid untuk aplikasi itu. Itu tidak memeriksa bagaimana aplikasi melukisnya, jadi lihat tata letak baru dalam sesi nyata juga.

Uji coba gambar setelah /clear

Setiap tes dimulai dengan setiap nilai $.state pada default-nya, yang merupakan cara /clear meninggalkannya. Untuk menguji apa yang dilakukan mod Anda selanjutnya, lewati session.start, naikkan classic.SessionStart dengan source: 'clear', dan periksa apa yang digambar mod Anda. Tes ini memeriksa modul dari Muat nilai yang disimpan lagi setelah /clear. Tambahkan ke file dari Uji coba gambar, di mana PANE didefinisikan. Tes pertama file itu mengharapkan tombol untuk menyimpan hitungan, seperti tombol dalam Simpan dari lebih dari satu sesi:
hello-tabs/tests/hello-tabs.test.ts
Tes lulus ketika hook classic.SessionStart Anda telah menyalin 7 yang disimpan ke $.state sebelum panel menggambar. Tanpa hook itu dalam modul Anda, panel menggambar Count: 0, find mengembalikan undefined, dan tes gagal di toBeDefined.

Uji coba mod yang menilai mod lain

Mod yang tercantum organisasi Anda dalam prependPlugins dapat menolak mod lain sebelum dimuat. Untuk menguji satu, atur tier mod Anda dan berikan tes mod kedua untuk diterima atau ditolak:
  • tier: panggil sekali di bagian atas file tes, seperti dalam tier('prepend'), untuk memuat mod Anda sebagai prepend, append, atau builtin, tempatnya dalam urutan mod berjalan. Tanpa itu, mod Anda memuat sebagai user.
  • plugins: teruskan test objek opsi di depan badan tes. Array plugins menyimpan mod yang Anda tulis inline, masing-masing dengan name dan fungsi register. Untuk memuat satu di tempat lain selain user, tambahkan tier ke itu.
File tes ini memuat mod kebijakan dari halaman admin terlebih dahulu. Itu memeriksa bahwa mod kebijakan menolak mod yang memulai proses dan mengakui yang tidak:
acme-guard/tests/guard.test.ts
Di shell Anda, jalankan claude plugin test dari direktori acme-guard. Kedua tes lulus dengan mod kebijakan seperti yang ditunjukkan halaman admin. Kit memuat setiap mod pada panggilan pertama tes pada $. Ketika mod Anda menolak satu, panggilan itu melempar, dan pesan menamai mod yang ditolak, mod yang menolaknya, dan alasan Anda. Dalam tes kedua tidak ada yang ditolak, jadi reader menjawab panggilan alat sebelum mencapai stub.

Langkah berikutnya