> ## Documentation Index
> Fetch the complete documentation index at: https://code.claude.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Einen Mod testen

> Schreiben Sie automatisierte Tests für einen Claude Code Mod, der Ereignisse auslöst, Antworten von Claude Code simuliert und Schaltflächen drückt, ohne Sitzung, Anmeldung oder Netzwerk.

Sie können automatisierte Tests für einen Mod schreiben und diese von Ihrer Shell aus mit [`claude plugin test`](/docs/de/plugins/mods/reference#commands) 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](/docs/de/plugins/mods/create).

<h2 id="write-a-test">
  Schreiben Sie einen Test
</h2>

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 mit `claude 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](/docs/de/plugins/mods/create) aus und prüft, dass die Antwort beide zählt. Seine erste Zeile ist ein [Stub](#stub-what-claude-code-would-answer), der die Tool-Aufrufe an Stelle von Claude Code beantwortet. Speichern Sie ihn als `first-mod/tests/first-mod.test.ts`:

```typescript first-mod/tests/first-mod.test.ts theme={null}
import { expect, test } from 'claude-code/testing'

test('/tally reports the tool calls the mod has seen', async ($, on) => {
  // Answer each tool call in Claude Code's place, so no tool runs
  on('tool.call', () => ({ result: 'ok' }))

  // Raise two tool calls, which the mod's tool.call hook counts
  await $.tool.call({ tool: 'Bash', command: 'ls' })
  await $.tool.call({ tool: 'Read', file_path: 'README.md' })

  // Run /tally and check the text its hook returns
  const answer = await $.command.run({ command: 'tally', args: '' })
  expect(answer.text).toBe('Claude has made 2 tool calls since this mod loaded')
})
```

Führen Sie in Ihrer Shell die Tests aus dem Verzeichnis `first-mod` aus:

```bash theme={null}
claude plugin test
```

Die Ausgabe nennt jeden Test und ob er bestanden hat, mit Zeitangaben, die von Durchlauf zu Durchlauf variieren:

```text theme={null}
tests/first-mod.test.ts:
(pass) /tally reports the tool calls the mod has seen [22.87ms]

 1 pass
 0 fail
Ran 1 test across 1 file. [0.19s]
```

Jeder `$.tool.call` durchlief den [`tool.call`](/docs/de/plugins/mods/reference#tools)-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`](/docs/de/plugins/mods/reference#commands-and-configuration)-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.

<h3 id="stub-what-claude-code-would-answer">
  Stub was Claude Code antworten würde
</h3>

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](/docs/de/plugins/mods/reference#mods-api-methods), 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öst `tool.call` aus. `$.command.run`, `$.prompt.submit`, `$.session.start` und `$.turn.complete` funktionieren auf die gleiche Weise, und `$.classic.Stop` und die anderen `$.classic`-Methoden lösen ein [Einstellungs-Hook-Ereignis](/docs/de/plugins/mods/events#hook-the-settings-hook-events) aus. Ein Test kann einen Mods-API-Aufruf wie `ui.close` nicht 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 als `store.get` registrierter Stub die `$.store.get` Ihres Mods beantwortet. Wenn Ihr Mod [`$.model.complete`](/docs/de/plugins/mods/api#call-a-model) oder [`$.store.get`](/docs/de/plugins/mods/interface#keep-state) aufruft, liefert ein Stub die Antwort.

Dieses Beispiel stubbt einen Modellaufruf. Der Hook gehört zu einem Mod namens `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](/docs/de/plugins/mods/create#write-a-mod-yourself). Um `/grade` in einer Sitzung einzugeben, muss das Mod auch den [Befehl registrieren](/docs/de/plugins/mods/api#add-a-command):

```javascript grader/hooks/register.js theme={null}
export function register(on) {
  on('command.run', { command: 'grade' }, async ($, e) => {
    // e.args is the text typed after /grade
    const reply = await $.model.complete({
      model: 'haiku',
      system: 'Grade the sentence. Start your reply with PASS or FAIL.',
      prompt: e.args,
    })
    const passed = reply.isAnswered && reply.text.startsWith('PASS')
    return { text: passed ? 'Passed' : 'Try again' }
  })
}
```

Dieser Test stubbt den Modellaufruf, um zu prüfen, was der Hook mit einer bestandenen Antwort tut:

```typescript grader/tests/grader.test.ts theme={null}
import { expect, test } from 'claude-code/testing'

test('a passing grade is reported', async ($, on) => {
  // Answer the mod's $.model.complete call with a fixed reply, so no model runs
  on('model.complete', () => ({
    value: {
      isAnswered: true,
      text: 'PASS\nNice sentence.',
      usage: { input_tokens: 10, output_tokens: 5, cache_read_input_tokens: 0, cache_creation_input_tokens: 0 },
    },
  }))

  // Run /grade, which makes the mod call the model
  const answer = await $.command.run({ command: 'grade', args: 'The cat sat on the mat.' })
  expect(answer.text).toBe('Passed')
})
```

Der Test besteht, weil die `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`](/docs/de/plugins/mods/reference#turns) 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](#look-up-what-a-stub-returns) 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ück
* `no implementation for` gefolgt von einem Namen: Ihr Mod hat diesen Aufruf gemacht und kein Stub beantwortet ihn

Das Kit exportiert auch speicherinterne Mocks, die einen ganzen Namespace für Sie beantworten. `mock.clock(on)` beantwortet [`$.clock`](/docs/de/plugins/mods/api#run-work-in-the-background), `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](#test-a-drawing) es tut.

<h3 id="follow-the-test-kit’s-rules">
  Befolgen Sie die Regeln des Test-Kits
</h3>

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 von `on` danach wirft einen Fehler wie `on("ui.render") after the test first called $`.

* **[`session.start`](/docs/de/plugins/mods/reference#session) wird 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, was `session.start` einrichtet, lösen Sie es zuerst aus:

  ```typescript theme={null}
  // Answer the event after your hook passes it on with next(e)
  on('session.start', () => ({ cwd: '/work' }))
  // Answer the $.command.register call your hook makes
  on('command.register', () => ({ value: undefined }))
  // Raise the event, which runs your session.start hook
  await $.session.start({ surface: 'terminal', isInteractive: true, cwd: '/work' })
  ```

  Der zweite Stub beantwortet den `$.command.register`-Aufruf, den ein `session.start`-Hook wie der [Tutorial](/docs/de/plugins/mods/create#write-a-mod-yourself) macht. Ohne ihn wird dieser Aufruf mit `no implementation for command.register` abgelehnt 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 unter `the 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 Ihr [`ui.render`](/docs/de/plugins/mods/reference#interface)-Hook `next(e)` zurückgibt, z. B. um nichts zu zeichnen, während Claude untätig ist, schlägt das [Mounten](#test-a-drawing) mit `no implementation for ui.render` fehl. Registrieren Sie einen Stub, der ein Element als einfache Daten zurückgibt:

  ```typescript theme={null}
  // Stands for what Claude Code would draw at the site
  on('ui.render', () => ({ type: 'Text', props: {}, children: ['drawn by Claude Code'] }))
  ```

  Mit dem registrierten Stub wird das Mounten erfolgreich, und `ui.find({ type: 'Text' })` gibt dieses Element zurück, wann immer Ihr Hook `next(e)` zurückgab.

* **Ein Stub für `turn.step` ist ein asynchroner Generator**, und der Test liest den Stream bis zum Ende, um das Ergebnis zu erhalten:

  ```typescript theme={null}
  on('turn.step', async function* ($, e) {
    // Each yield is one piece of the model's streamed reply
    yield { kind: 'text', index: 0, text: 'ok' }
    // The return value is the result of the whole request
    return { turnId: e.turnId, index: e.index, answer: 'ok', toolUses: [], stopReason: 'end_turn', usage: null }
  })

  // Raise one request to the model, which runs your turn.step hook
  const stream = $.turn.step({ turnId: 't', index: 0, model: 'claude-test', messageCount: 1 })
  // Read every piece until the stream says it's done
  let step = await stream.next()
  while (step.done !== true) step = await stream.next()
  const result = step.value
  ```

  Wenn die Schleife endet, ist `result` das Objekt, das der Stub zurückgab, nachdem Ihr `turn.step`-Hook die Chance hatte, es zu ändern. Hier ist `result.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 einen `tool.call`-Stub, der `{ result }` zurückgibt.

<h3 id="look-up-what-a-stub-returns">
  Nachschlagen, was ein Stub zurückgibt
</h3>

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`](/docs/de/plugins/mods/interface#redraw-when-something-changes) und [`$.state`](/docs/de/plugins/mods/interface#keep-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:

| Ihr Mod ruft auf oder gibt weiter | Stub |
| :- | :- |
| `$.command.register`, `$.tool.register`, `$.ui.toast`, `$.ui.log`, `$.ui.status`, `$.ui.close`, `$.store.set` | `() => ({ value: undefined })`. Für `ui.toast` und `ui.log` ist der Text `e.text`. |
| `$.store.get` | `($, e) => ({ value: saved.get(e.key) })` |
| `$.fs.read` | `($, e) => ({ value: e.path.endsWith('notes.md') ? '# Notes' : '' })`. `e.path` kommt als absoluter Pfad an, daher vergleichen Sie mit `endsWith`. |
| `$.ui.open` | `() => ({ value: { isPlaced: true } })` |
| `$.ui.ask` | Ein `tool.call`-Stub, weil die Frage es als Aufruf des `AskUserQuestion`-Tools erreicht: `($, e) => ({ result: { answers: { [e.questions[0].question]: 'Run it' } } })`. Prüfen Sie zuerst `e.tool`, wenn Ihr Mod andere Tool-Aufrufe weitergegeben wird. |
| `$.model.complete` | `() => ({ value: { isAnswered: true, text: '...', usage } })` |
| `$.process.run` | `($, e) => ({ value: { exitCode: 0, stdout: '...', stderr: '' } })`. `e.argv` ist die Argumentliste und `e.init` hält `cwd` und `timeoutMs`. |
| Jeder Mods-API-Aufruf, der fehlschlagen sollte | `() => ({ deny: 'the reason' })`, was den Aufruf in Ihrem Mod ablehnen lässt. Ein Stub, der wirft, wird stattdessen übersprungen. |
| `session.start` | `() => ({ cwd: '/work' })` |
| `turn.start` | `($, e) => ({ turnId: e.turnId })` |
| `tool.call` | `() => ({ result: '...' })` |
| `turn.complete` | `() => ({ text: '' })`. Lösen Sie es mit `$.turn.complete({ turnId, answer, durationMs, isAborted: false, usage: null })` aus. |
| `prompt.submit` | `($, e) => ({ text: e.text })` |
| `prompt.fill` | `() => ({ isFilled: true })` |
| `$.prompt.read` | `() => ({ value: { text: '...', cursor: 0 } })` |
| `$.ui.copy` | `() => ({ value: { isCopied: true } })` |
| `$.session.messages` | `() => ({ value: [{ role: 'assistant', text: '...', toolUses: [] }] })` |
| `$.session.id`, `$.agent.list` | `() => ({ value: 'abc123' })`, `() => ({ value: [] })` |
| `session.send` | `() => ({ isDelivered: true })`. `e.to` kommt als String an, auch wenn Ihr Mod `{ sessionId }` weitergegeben hat. |
| `session.receive` | `($, e) => ({ text: e.text })`. Lösen Sie es mit `$.session.receive({ origin: { kind: 'peer-send-message' }, text })` aus. |
| `ui.render` | `() => ({ type: 'Text', props: {}, children: ['...'] })` |

`expect` hat die Assertions `toBe`, `toEqual`, `toMatch`, `toMatchObject`, `toContain`, `toBeDefined`, `toBeUndefined` und `toThrow`, und `.not` vor jedem von ihnen.

<h2 id="test-a-timer">
  Einen Timer testen
</h2>

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:

| Methode | Was sie tut |
| :- | :- |
| `await clock.advance(1000)` | Bewegt die Zeit um diese viele Millisekunden vorwärts und führt jeden Timer aus, der fällig wird |
| `await clock.set(5000)` | Bewegt die Zeit vorwärts zu diesem Wert, wie `advance` es würde |
| `clock.now()` | Gibt die Zeit zurück, die Ihr Mods `$.clock.now()` zu aufgelöst wird |
| `await clock.settle()` | Führt Timer aus, die bereits fällig sind, wie eine Kette von `$.clock.after`-Aufrufen mit Null-Verzögerung, ohne die Zeit zu bewegen |
| `await clock.sleep(2000)` | Innerhalb eines Stubs macht dies, dass dieser Stub nur antwortet, sobald der Test so weit vorangekommen ist, was ist, wie Sie ein langsames Modell oder einen langsamen Prozess simulieren |

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:

```javascript countdown/hooks/register.js theme={null}
export function register(on) {
  on('command.run', { command: 'countdown' }, async ($, e) => {
    // e.args is the text typed after /countdown
    let left = Number(e.args)
    const timer = $.clock.every(1000, () => {
      left -= 1
      if (left === 0) {
        timer.cancel()
        $.ui.toast('Time is up')
      }
    })
    // Print nothing in the transcript
    return {}
  })
}
```

Dieser Test führt `/countdown 3` aus und bewegt die Mock-Uhr, sodass er drei Sekunden Verhalten überprüft, ohne drei Sekunden zu warten:

```typescript countdown/tests/countdown.test.ts theme={null}
import { expect, mock, test } from 'claude-code/testing'

test('the countdown ends with a toast', async ($, on) => {
  // Answer every $.clock call from a clock the test controls
  const clock = mock.clock(on)
  // Collect the text of each toast the mod shows
  const toasts: string[] = []
  on('ui.toast', ($, e) => {
    toasts.push(e.text)
    return { value: undefined }
  })

  await $.command.run({ command: 'countdown', args: '3' })
  // After two seconds the timer has fired twice, and no toast is due
  await clock.advance(2000)
  expect(toasts).toEqual([])
  // The third second brings the count to zero
  await clock.advance(1000)
  expect(toasts).toEqual(['Time is up'])
})
```

Das erste `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.

<h2 id="test-a-drawing">
  Eine Zeichnung testen
</h2>

Ein Test kann eine der [Render-Stellen](/docs/de/plugins/mods/reference#render-sites) 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](/docs/de/plugins/mods/interface#build-a-pane-with-tabs), wechselt Registerkarten, drückt die Schaltfläche und überprüft die Zählung im Terminal und der Desktop-App:

```typescript hello-tabs/tests/hello-tabs.test.ts theme={null}
import { expect, test } from 'claude-code/testing'

// What Claude Code passes to a ui.render hook for this pane, apart from the app
const PANE = {
  plugin: 'hello-tabs',
  component: 'Pane',
  requestId: 'hello-tabs',
  viewport: { columns: 100, rows: 30 },
  props: {
    title: 'Hello tabs',
    isFocused: true,
    bodyColumns: 60,
    placement: 'inline',
    scroll: { offset: 0, bodyRows: 10 },
    view: {},
  },
} as const

test('the second tab counts presses and saves the count', async ($, on) => {
  // Stub $.store with a Map, so the test can read what the mod saved
  const saved = new Map<string, unknown>()
  on('store.get', ($, e) => ({ value: saved.get(e.key) }))
  on('store.set', ($, e) => {
    saved.set(e.key, e.value)
    return { value: undefined }
  })

  // Draw the pane once for each app
  for (const surface of ['terminal', 'desktop'] as const) {
    const ui = await $.ui.mount({ ...PANE, surface })
    // Press the buttons by the key the mod gave them
    await ui.press({ key: 'tab-two' })
    await ui.press({ key: 'more' })
    // The second tab's count line is in the drawing
    expect(await ui.find({ type: 'Text', text: /^Count: \d+$/ })).toBeDefined()
    await ui.unmount()
  }

  // One press in each app makes two
  expect(saved.get('count')).toBe(2)
})
```

Führen Sie in Ihrer Shell `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:

| Methode | Was sie tut |
| :- | :- |
| `press({ key: 'more' })` | Drückt die `Button` mit diesem Key |
| `input({ key: 'new-note', text: 'buy milk' })` | Gibt den Text in die `Input` mit diesem Key ein und drückt Enter. Fügen Sie `kind: 'change'` hinzu, um einzugeben, ohne zu senden. |
| `select({ key: 'size', value: 'large' })` | Wählt die Option mit diesem Wert in der `Select` mit diesem Key |
| `find({ key: 'more' })` oder `find({ type: 'Text', text: 'Count: 2' })` | Gibt das erste übereinstimmende Element als `{ type, props, children }` zurück, oder `undefined`. `text` kann ein String oder ein regulärer Ausdruck sein. |
| `unmount()` | Entfernt die Zeichnung |

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](/docs/de/plugins/mods/reference#render-sites) listet die Props jeder Stelle auf, und [die Typen für Ihren Build](/docs/de/plugins/mods/create#get-the-types-for-your-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.

<h3 id="test-a-drawing-after-clear">
  Eine Zeichnung nach `/clear` testen
</h3>

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](/docs/de/plugins/mods/interface#load-a-saved-value-again-after-clear). Fügen Sie es zur Datei aus [Eine Zeichnung testen](#test-a-drawing) 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](/docs/de/plugins/mods/interface#save-from-more-than-one-session) es tut:

```typescript hello-tabs/tests/hello-tabs.test.ts theme={null}
test('the saved count comes back after /clear', async ($, on) => {
  // The store already holds a count of 7
  on('store.get', () => ({ value: 7 }))
  // Answer the event after your hook passes it on with next(e)
  on('classic.SessionStart', () => ({}))

  // Raise the event that fires after /clear, which runs your hook
  await $.classic.SessionStart({ source: 'clear' })

  const ui = await $.ui.mount({ ...PANE, surface: 'terminal' })
  await ui.press({ key: 'tab-two' })
  // The pane shows the stored count, not the default of 0
  expect(await ui.find({ type: 'Text', text: 'Count: 7' })).toBeDefined()
})
```

Der Test besteht, wenn Ihr `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.

<h2 id="test-a-mod-that-judges-other-mods">
  Einen Mod testen, der andere Mods beurteilt
</h2>

Ein Mod, den Ihre Organisation in [`prependPlugins`](/docs/de/plugins/mods/admin) 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 in `tier('prepend')`, um Ihren Mod als `prepend`, `append` oder `builtin` zu laden, seinen Platz in der [Reihenfolge, in der Mods ausgeführt werden](/docs/de/plugins/mods/events#the-order-mods-run-in). Ohne ihn wird Ihr Mod als `user` geladen.
* **`plugins`**: übergeben Sie `test` ein Optionsobjekt vor dem Test-Body. Sein `plugins`-Array hält Mods, die Sie inline schreiben, jeder mit einem `name` und einer `register`-Funktion. Um einen an einer anderen Stelle als `user` zu laden, fügen Sie `tier` hinzu.

Diese Testdatei lädt den [Policy-Mod von der Admin-Seite](/docs/de/plugins/mods/admin#enforce-a-policy-with-a-mod-of-your-own) zuerst. Sie überprüft, dass der Policy-Mod einen Mod ablehnt, der einen Prozess startet, und einen zulässt, der nicht:

```typescript acme-guard/tests/guard.test.ts theme={null}
import { expect, test, tier } from 'claude-code/testing'

// Load the mod under test ahead of every other mod
tier('prepend')

// A second mod whose code calls $.process.run, which the policy blocks
const runner = {
  name: 'runner',
  register(on) {
    on('tool.call', async ($, e, next) => {
      await $.process.run(['ls'])
      return { result: 'runner answered' }
    })
  },
}

// A second mod that calls nothing the policy blocks
const reader = {
  name: 'reader',
  register(on) {
    on('tool.call', async ($, e, next) => {
      return { result: 'reader answered' }
    })
  },
}

test('refuses a mod that starts a process', { plugins: [runner] }, async ($, on) => {
  on('tool.call', () => ({ result: 'claude code answered' }))
  let message = ''
  try {
    // The first call on $ loads the mods, so the refusal is thrown here
    await $.tool.call({ tool: 'Bash', command: 'ls' })
  } catch (error) {
    message = error.message
  }
  expect(message).toBe('runner: refused by acme-guard: Acme policy: mods may not call process.run')
})

test('admits a mod that starts no process', { plugins: [reader] }, async ($, on) => {
  on('tool.call', () => ({ result: 'claude code answered' }))
  const out = await $.tool.call({ tool: 'Bash', command: 'ls' })
  // The answer comes from reader, which shows that it loaded
  expect(out).toEqual({ result: 'reader answered' })
})
```

Führen Sie in Ihrer Shell `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.

<h2 id="next-steps">
  Nächste Schritte
</h2>

* [Einen Mod beheben](/docs/de/plugins/mods/troubleshoot): finden Sie heraus, warum ein Mod in einer Sitzung nichts tut
* [Mods-Referenz](/docs/de/plugins/mods/reference): jedes Ereignis-Input und Ergebnis zum Schreiben von Stubs
