> ## 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.

# Testar um mod

> Escreva testes automatizados para um mod Claude Code que levantam eventos, simulam respostas do Claude Code e pressionam botões, sem sessão, login ou rede.

Você pode escrever testes automatizados para um mod e executá-los a partir do seu shell com [`claude plugin test`](/docs/pt/plugins/mods/reference#commands). Um teste levanta os eventos que seus hooks tratam e verifica o que os hooks fizeram, para que você detecte um problema antes que ele chegue a uma sessão. O primeiro exemplo testa o mod de [Create a mod](/docs/pt/plugins/mods/create).

<h2 id="write-a-test">
  Escrever um teste
</h2>

Um teste carrega seu mod, envia eventos através de seus hooks da forma como Claude Code faria, e verifica o que os hooks fizeram, sem uma sessão, um login ou uma rede. Você executa testes a partir do seu shell com `claude plugin test`, e cada arquivo de teste importa o test kit, uma biblioteca de teste no módulo `claude-code/testing`.

Dê a cada arquivo de teste um nome que termine em `.test.ts`, como `first-mod.test.ts`, e salve-o em qualquer lugar no diretório do plugin. Cada arquivo de teste precisa de pelo menos um `test()`, ou a execução falha com `declares no test(): nothing ran`. Um arquivo de teste pode importar seus próprios arquivos do mod e helpers `.ts` irmãos, para que você possa fazer testes unitários de funções simples, como as regras de um jogo, sem o kit.

Este teste levanta duas chamadas de ferramenta, executa o comando `/tally` de [Create a mod](/docs/pt/plugins/mods/create), e verifica se a resposta conta ambas. Sua primeira linha é um [stub](#stub-what-claude-code-would-answer), que responde as chamadas de ferramenta no lugar do Claude Code. Salve-o como `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')
})
```

No seu shell, execute os testes a partir do diretório `first-mod`:

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

A saída nomeia cada teste e se passou, com tempos que variam de execução para execução:

```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]
```

Cada `$.tool.call` passou pelo hook [`tool.call`](/docs/pt/plugins/mods/reference#tools) do mod, que adicionou um à sua contagem e passou a chamada para o stub. Nenhum `ls` foi executado e nenhum arquivo foi lido. `$.command.run` então foi para o hook [`command.run`](/docs/pt/plugins/mods/reference#commands-and-configuration) do mod, e `answer` é o objeto que esse hook retornou.

O comando sai com status 1 quando um teste falha, então funciona em CI. Se seus próprios mods não conseguirem carregar no shell que o executa, ele imprime uma linha começando com `claude plugin test: hooks modules are turned off` com o motivo, e sai com status 1.

<h3 id="stub-what-claude-code-would-answer">
  Simular o que Claude Code responderia
</h3>

Nenhum modelo, armazenamento ou ferramenta é executado em um teste, então onde quer que seu mod espere que Claude Code responda, o teste fornece a resposta com um stub. Uma função de teste recebe dois argumentos para isso:

* **`$`**: o próprio `$` do teste, que fica no lugar do Claude Code. Não é a [mods API](/docs/pt/plugins/mods/reference#mods-api-methods) que um hook recebe. Cada um de seus métodos levanta o evento de mesmo nome, o envia através dos hooks do seu mod, e resolve para o resultado: `$.tool.call({ tool: 'Bash', command: 'ls' })` levanta `tool.call`. `$.command.run`, `$.prompt.submit`, `$.session.start`, e `$.turn.complete` funcionam da mesma forma, e `$.classic.Stop` e os outros métodos `$.classic` levantam um [evento de hook de configurações](/docs/pt/plugins/mods/events#hook-the-settings-hook-events). Um teste não pode levantar uma chamada de mods API como `ui.close` diretamente. Dispare-a através do seu mod, por exemplo pressionando o botão que fecha o painel.
* **`on`**: chame-o para registrar stubs, que são hooks que respondem no lugar do Claude Code. Nomeie um stub para uma chamada de mods API sem o `$.`, então um stub registrado como `store.get` responde seu `$.store.get` do mod. Quando seu mod chama [`$.model.complete`](/docs/pt/plugins/mods/api#call-a-model) ou [`$.store.get`](/docs/pt/plugins/mods/interface#keep-state), um stub fornece a resposta.

Este exemplo simula uma chamada de modelo. O hook pertence a um mod chamado `grader`, e trata um comando `/grade` que envia uma frase para um modelo e relata se a resposta começa com `PASS`. O arquivo contém apenas o hook sob teste, então o mod também precisa de um `plugin.json` e um `hooks.json`, como em [Create a mod](/docs/pt/plugins/mods/create#write-a-mod-yourself). Para digitar `/grade` em uma sessão, o mod também tem que [registrar o comando](/docs/pt/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' }
  })
}
```

Este teste simula a chamada do modelo para verificar o que o hook faz com uma resposta aprovada:

```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')
})
```

O teste passa porque o `reply` do hook é o objeto sob `value`, cujo `text` começa com `PASS`. Para verificar o outro ramo, adicione um segundo teste cujo stub retorna um `text` que começa com `FAIL`, e espere `Try again`.

Um stub para uma chamada de mods API retorna um objeto com um campo `value`, que contém o que a chamada resolve em seu mod: `{ value: 7 }` faz `$.store.get` resolver para `7`. Um stub para um dos eventos do Claude Code, como [`turn.step`](/docs/pt/plugins/mods/reference#turns) ou `tool.call`, retorna o resultado próprio desse evento, como `{ result: 'ok' }`. `$.session.send` e `$.prompt.fill` também levam o resultado do evento, como a tabela mostra. [Look up what a stub returns](#look-up-what-a-stub-returns) mostra qual forma cada nome comum assume. Dois erros significam que um stub está errado ou faltando. A saída de um teste falhado inclui um bloco intitulado `the engine reported:`, e cada erro aparece lá:

* `returned neither { value } nor { deny }`: um stub para uma chamada de mods API retornou um valor simples
* `no implementation for` seguido por um nome: seu mod fez essa chamada e nenhum stub a responde

O kit também exporta mocks em memória que respondem um namespace inteiro para você. `mock.clock(on)` responde [`$.clock`](/docs/pt/plugins/mods/api#run-work-in-the-background), `mock.store(on, { count: 7 })` responde `$.store` de um armazenamento que começa com essas entradas, e `mock.env(on, { CI: 'true' })` responde `$.env.get` dessas variáveis. `mock.clock` retorna um relógio simulado que seu teste avança, então um teste de um temporizador não espera. `mock.store` não retorna nada, então para verificar o que seu mod salvou, escreva os dois stubs `store` você mesmo como o [drawing test](#test-a-drawing) faz.

<h3 id="follow-the-test-kit’s-rules">
  Seguir as regras do test kit
</h3>

O test kit tem algumas regras próprias, e quebrar uma produz os erros que novos autores de testes encontram primeiro:

* **Registre cada stub antes da primeira chamada do teste em `$`.** Chamar `on` depois disso lança um erro como `on("ui.render") after the test first called $`.

* **[`session.start`](/docs/pt/plugins/mods/reference#session) não é executado por si só.** Cada teste começa com seu módulo carregado recentemente e nenhum de seus hooks chamado, então variáveis de nível de módulo mantêm seus valores iniciais. Se um hook depende do que `session.start` configura, levante-o primeiro:

  ```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' })
  ```

  O segundo stub responde a chamada `$.command.register` que um hook `session.start` como o do [tutorial](/docs/pt/plugins/mods/create#write-a-mod-yourself) faz. Sem ele, essa chamada rejeita com `no implementation for command.register` e o kit pula seu hook, então nada depois da chamada no hook é executado. O teste não falha nesse ponto. O hook pulado é listado sob `the engine reported:` apenas se uma verificação posterior falhar.

* **Um hook que retorna `next(e)` precisa de um stub para responder.** Quando seu hook [`ui.render`](/docs/pt/plugins/mods/reference#interface) retorna `next(e)`, por exemplo para não desenhar nada enquanto Claude está ocioso, [montá-lo](#test-a-drawing) falha com `no implementation for ui.render`. Registre um stub que retorna um elemento como dados simples:

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

  Com o stub registrado, a montagem é bem-sucedida, e `ui.find({ type: 'Text' })` retorna esse elemento sempre que seu hook retornou `next(e)`.

* **Um stub para `turn.step` é um gerador assíncrono**, e o teste lê o fluxo até o final para obter o resultado:

  ```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
  ```

  Quando o loop termina, `result` é o objeto que o stub retornou, depois que seu hook `turn.step` teve a chance de alterá-lo. Aqui `result.answer` é `'ok'`.

* **Levante uma chamada de ferramenta com o nome da ferramenta e argumentos como campos**, como `await $.tool.call({ tool: 'Bash', command: 'ls' })`, e registre um stub `tool.call` que retorna `{ result }`.

<h3 id="look-up-what-a-stub-returns">
  Procurar o que um stub retorna
</h3>

Cada chamada de mods API que seu mod faz em um teste precisa de um stub que responda no lugar do Claude Code, exceto as poucas que o kit responde por si: chamadas [`$.ui.invalidate`](/docs/pt/plugins/mods/interface#redraw-when-something-changes) e [`$.state`](/docs/pt/plugins/mods/interface#keep-state). Para chamadas `$.clock`, use `mock.clock(on)`, ou seu `$.clock.now()` do mod falha com `no implementation for clock.now`.

Esta tabela lista as que mods usam mais. A primeira coluna é a chamada que seu mod faz ou o evento que passa com `next(e)`. A segunda é a função para passar para `on` sob esse nome, então a linha `$.store.get` se torna `on('store.get', ($, e) => ({ value: saved.get(e.key) }))`. Um `'...'` em um stub marca texto para você preencher:

| Seu mod chama ou passa | Stub |
| :- | :- |
| `$.command.register`, `$.tool.register`, `$.ui.toast`, `$.ui.log`, `$.ui.status`, `$.ui.close`, `$.store.set` | `() => ({ value: undefined })`. Para `ui.toast` e `ui.log`, o texto é `e.text`. |
| `$.store.get` | `($, e) => ({ value: saved.get(e.key) })` |
| `$.fs.read` | `($, e) => ({ value: e.path.endsWith('notes.md') ? '# Notes' : '' })`. `e.path` chega como um caminho absoluto, então compare com `endsWith`. |
| `$.ui.open` | `() => ({ value: { isPlaced: true } })` |
| `$.ui.ask` | Um stub `tool.call`, porque a pergunta a alcança como uma chamada para a ferramenta `AskUserQuestion`: `($, e) => ({ result: { answers: { [e.questions[0].question]: 'Run it' } } })`. Verifique `e.tool` primeiro se seu mod passa outras chamadas de ferramenta. |
| `$.model.complete` | `() => ({ value: { isAnswered: true, text: '...', usage } })` |
| `$.process.run` | `($, e) => ({ value: { exitCode: 0, stdout: '...', stderr: '' } })`. `e.argv` é a lista de argumentos e `e.init` contém `cwd` e `timeoutMs`. |
| Qualquer chamada de mods API que deve falhar | `() => ({ deny: 'the reason' })`, que faz a chamada rejeitar em seu mod. Um stub que lança é pulado. |
| `session.start` | `() => ({ cwd: '/work' })` |
| `turn.start` | `($, e) => ({ turnId: e.turnId })` |
| `tool.call` | `() => ({ result: '...' })` |
| `turn.complete` | `() => ({ text: '' })`. Levante-o com `$.turn.complete({ turnId, answer, durationMs, isAborted: false, usage: null })`. |
| `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` chega como uma string mesmo quando seu mod passou `{ sessionId }`. |
| `session.receive` | `($, e) => ({ text: e.text })`. Levante-o com `$.session.receive({ origin: { kind: 'peer-send-message' }, text })`. |
| `ui.render` | `() => ({ type: 'Text', props: {}, children: ['...'] })` |

`expect` tem as asserções `toBe`, `toEqual`, `toMatch`, `toMatchObject`, `toContain`, `toBeDefined`, `toBeUndefined`, e `toThrow`, e `.not` antes de qualquer uma delas.

<h2 id="test-a-timer">
  Testar um temporizador
</h2>

Um mod que executa trabalho em um temporizador precisa de um relógio que o teste controla, para que o teste possa avançar o tempo em vez de esperar. `const clock = mock.clock(on)` retorna um relógio simulado que começa em `0` e se move apenas quando seu teste o move. Para começar em outro tempo, passe-o em milissegundos, como em `mock.clock(on, { now: 5000 })`. O relógio tem estes métodos:

| Método | O que faz |
| :- | :- |
| `await clock.advance(1000)` | Avança o tempo por esse número de milissegundos e executa cada temporizador que vence |
| `await clock.set(5000)` | Avança o tempo para esse valor, como `advance` faria |
| `clock.now()` | Retorna o tempo, que é o que seu `$.clock.now()` do mod resolve para |
| `await clock.settle()` | Executa temporizadores que já vencem, como uma cadeia de chamadas `$.clock.after` de zero atraso, sem mover o tempo |
| `await clock.sleep(2000)` | Dentro de um stub, faz esse stub responder apenas uma vez que o teste avançou tão longe, que é como você simula um modelo ou processo lento |

Este hook pertence a um mod chamado `countdown`, e trata um comando `/countdown` que leva um número de segundos, inicia um temporizador `$.clock.every` de um segundo, e mostra um toast em zero. Como com `grader`, o arquivo contém apenas o hook sob teste e não registra o comando:

```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 {}
  })
}
```

Este teste executa `/countdown 3` e move o relógio simulado, então verifica três segundos de comportamento sem esperar três segundos:

```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'])
})
```

O primeiro `expect` mostra que o toast não vem cedo, e o segundo mostra que vem uma vez. Cada `advance` resolve depois que os temporizadores que vencem foram executados, então a verificação na próxima linha vê seu efeito.

<h2 id="test-a-drawing">
  Testar um desenho
</h2>

Um teste pode desenhar um dos [render sites](/docs/pt/plugins/mods/reference#render-sites) do seu mod, então pressionar, digitar em e encontrar os elementos que desenhou. `$.ui.mount` desenha o site através do hook `ui.render` do seu mod e retorna um identificador com um método para cada um desses. Para cobrir vários aplicativos em um teste, defina `surface` para o aplicativo a desenhar. Este teste abre o painel de [Build a pane with tabs](/docs/pt/plugins/mods/interface#build-a-pane-with-tabs), muda abas, pressiona o botão, e verifica a contagem no terminal e no aplicativo Desktop:

```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)
})
```

No seu shell, execute `claude plugin test` a partir do diretório `hello-tabs`. O teste passa quando ambos os aplicativos desenham a linha de contagem e o mod salvou `2`. A contagem é transferida do primeiro aplicativo para o segundo porque ambas as montagens usam o mesmo módulo carregado.

O identificador que `$.ui.mount` retorna tem estes métodos, que endereçam elementos pela `key` que você deu a eles:

| Método | O que faz |
| :- | :- |
| `press({ key: 'more' })` | Pressiona o `Button` com essa chave |
| `input({ key: 'new-note', text: 'buy milk' })` | Digita o texto no `Input` com essa chave e pressiona Enter. Adicione `kind: 'change'` para digitar sem enviar. |
| `select({ key: 'size', value: 'large' })` | Escolhe a opção com esse valor no `Select` com essa chave |
| `find({ key: 'more' })` ou `find({ type: 'Text', text: 'Count: 2' })` | Retorna o primeiro elemento correspondente como `{ type, props, children }`, ou `undefined`. `text` pode ser uma string ou uma expressão regular. |
| `unmount()` | Remove o desenho |

Cada método resolve depois que seu manipulador terminou, então você pode verificar o resultado na próxima linha. Defina `props` para o que Claude Code passaria para esse site. A [tabela de render sites](/docs/pt/plugins/mods/reference#render-sites) lista os props de cada site, e [os tipos para sua compilação](/docs/pt/plugins/mods/create#get-the-types-for-your-build) têm seus tipos.

Um teste de desenho verifica a árvore que seu hook retorna e se é válida para esse aplicativo. Não verifica como o aplicativo a pinta, então veja um novo layout em uma sessão real também.

<h3 id="test-a-drawing-after-clear">
  Testar um desenho após `/clear`
</h3>

Cada teste começa com cada valor `$.state` em seu padrão, que é como `/clear` os deixa. Para testar o que seu mod faz a seguir, pule `session.start`, levante `classic.SessionStart` com `source: 'clear'`, e verifique o que seu mod desenha.

Este teste verifica o módulo de [Load a saved value again after `/clear`](/docs/pt/plugins/mods/interface#load-a-saved-value-again-after-clear). Adicione-o ao arquivo de [Test a drawing](#test-a-drawing), onde `PANE` é definido. O primeiro teste desse arquivo espera que o botão salve a contagem, como o botão em [Save from more than one session](/docs/pt/plugins/mods/interface#save-from-more-than-one-session) faz:

```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()
})
```

O teste passa quando seu hook `classic.SessionStart` copiou o `7` armazenado em `$.state` antes do painel desenhar. Sem esse hook em seu módulo, o painel desenha `Count: 0`, `find` retorna `undefined`, e o teste falha em `toBeDefined`.

<h2 id="test-a-mod-that-judges-other-mods">
  Testar um mod que julga outros mods
</h2>

Um mod que sua organização lista em [`prependPlugins`](/docs/pt/plugins/mods/admin) pode recusar outro mod antes que ele carregue. Para testar um, defina o nível do seu mod e dê ao teste um segundo mod para o seu admitir ou recusar:

* **`tier`**: chame-o uma vez no topo do arquivo de teste, como em `tier('prepend')`, para carregar seu mod como `prepend`, `append`, ou `builtin`, seu lugar na [ordem que mods são executados](/docs/pt/plugins/mods/events#the-order-mods-run-in). Sem ele, seu mod carrega como `user`.
* **`plugins`**: passe `test` um objeto de opções antes do corpo do teste. Seu array `plugins` contém mods que você escreve inline, cada um com um `name` e uma função `register`. Para carregar um em algum lugar diferente de `user`, adicione `tier` a ele.

Este arquivo de teste carrega o [policy mod da página admin](/docs/pt/plugins/mods/admin#enforce-a-policy-with-a-mod-of-your-own) primeiro. Verifica que o policy mod recusa um mod que inicia um processo e admite um que não:

```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' })
})
```

No seu shell, execute `claude plugin test` a partir do diretório `acme-guard`. Ambos os testes passam com o policy mod como a página admin mostra.

O kit carrega cada mod na primeira chamada do teste em `$`. Quando seu mod recusa um, essa chamada lança, e a mensagem nomeia o mod recusado, o mod que o recusou, e seu motivo. No segundo teste nada é recusado, então `reader` responde a chamada de ferramenta antes que chegue ao stub.

<h2 id="next-steps">
  Próximos passos
</h2>

* [Troubleshoot a mod](/docs/pt/plugins/mods/troubleshoot): descubra por que um mod não faz nada em uma sessão
* [Mods reference](/docs/pt/plugins/mods/reference): cada evento de entrada e resultado, para escrever stubs
