O que crucible faz?
Quando usar isto
Quando solicitado a escrever, corrigir ou auditar testes unitários em projetos TypeScript.
Quando NÃO usar isto
Ao escrever testes de ponta a ponta, integração, UI ou simulação.
Desativação por projeto
Antes de ativar, verifique se o projeto optou por desativar esta habilidade. Procure por qualquer um dos seguintes:
- Um arquivo
.no-cruciblena raiz do projeto "crucible": falseem qualquer lugar deAGENTS.mdou.claude/settings.json
Se algum sinal de desativação for encontrado, não ative automaticamente; apenas aplique esta habilidade se o usuário solicitar explicitamente nesta conversa.
Regras
- Os comandos de exemplo usam
pnpm, mas sempre use o gerenciador de pacotes do projeto. - Não modifique o código da aplicação a menos que seja especificamente solicitado.
- Quando solicitado a adicionar testes, não modifique os testes existentes.
- Quando solicitado a corrigir testes, não adicione novos testes.
- Quando solicitado a auditar testes, corrija os testes existentes e depois adicione testes conforme necessário para atender aos critérios de teste.
Critérios de Teste
Testes unitários devem:
- Ter a cobertura de branch máxima possível de acordo com Vitest.
- Matar todos os mutantes matáveis, alcançando a pontuação de mutação máxima possível em todo o código no escopo do prompt (não apenas o coberto pelos testes). Mutantes imatáveis impedirão uma pontuação de 100.
- Ser baseados em propriedades sempre que possível, exceto se houver apenas um punhado de casos de entrada. Por exemplo, uma função com um único parâmetro booleano e sem entradas implícitas tem apenas dois casos de entrada possíveis, então não se preocupe em torná-la baseada em propriedades.
- (se baseado em propriedades) ter arbitrariedades que cubram todos os parâmetros e entradas implícitas possíveis. É vital que nenhum caso que possa ocorrer em produção seja excluído. Se necessário ou incerto, erre para tornar as arbitrariedades muito amplas.
- Falhar se testarem código incorreto ou se as arbitrariedades forem propositalmente ampliadas demais. Pense nos testes como requisitos: eles devem ser nomeados de acordo e devem passar automaticamente assim que o usuário corrigir o bug no futuro, após corrigir a função e/ou as arbitrariedades. Não use
test.fails/.skip; isso torna menos óbvio para o usuário que atenção é necessária, então testes passando quando não deveriam é o erro de maior prioridade a ser evitado.
Verifique a cobertura de branch e execute os testes: pnpm exec vitest run src/foo.test.ts --coverage
Verifique os mutantes sobreviventes no código sendo testado: pnpm exec stryker run --mutate "src/foo.ts"
Verificando a configuração de teste
- Verifique se todos os seguintes pacotes estão instalados no projeto:
vitest fast-check @stryker-mutator/core @stryker-mutator/vitest-runner @stryker-mutator/typescript-checker - Verifique se as configurações do Vitest e do Stryker incluem/excluem arquivos corretamente. O Stryker não deve verificar arquivos que contenham apenas dados de teste.
- Verifique a configuração do Stryker:
- Use o reporter
clear-text(outros reporters são opcionais). - Use o TypeScript checker e aponte corretamente para o arquivo de configuração do TS.
- Use o reporter
- Procure por outros problemas de configuração que possam causar problemas no fluxo de trabalho de escrita de testes.
- Relate as descobertas ao usuário.
Escrevendo Testes
Quando solicitado a escrever novos testes, NÃO MODIFIQUE O CÓDIGO DA APLICAÇÃO NEM OS TESTES EXISTENTES.
Primeiro, avalie os testes existentes seguindo Avaliando um módulo. Crie um novo arquivo de debrief usando o modelo e registre a cobertura inicial e as pontuações de mutação lá.
Segundo, escreva os testes seguindo este processo exatamente:
Faça o seguinte para cada função/método na interface do módulo, terminando uma função antes de começar a próxima:
- Determine o propósito da função examinando comentários, seu nome, sua assinatura de tipo e como é usada. Se estiver incerto, anote isso no debrief, pule a escrita de testes e passe para a próxima função.
- Anote quaisquer bugs potenciais na implementação da função.
- Com base em seu propósito, identifique quaisquer suposições falhas que a função faz sobre seus parâmetros e entradas implícitas, e anote quaisquer casos de entrada que provavelmente levem a um comportamento incorreto da função. Por exemplo, uma função que recebe um inteiro positivo, mas não valida sua entrada, faz uma suposição falha sobre sua entrada.
- NUNCA MODIFIQUE TESTES PRÉ-EXISTENTES. Se você encontrar problemas nos testes existentes, notifique o usuário sobre os problemas encontrados.
- Escreva novos testes fazendo o seguinte:
a. Usando
fast-check, crie stubs de teste baseados em propriedades com arbitrariedades que cubram todos os parâmetros e entradas implícitas possíveis da função. Usefc.asyncProperty+await fc.assert(...)quando a função sob teste for assíncrona. Em caso de dúvida, erre para tornar as arbitrariedades muito amplas e mencione isso no debrief para que o usuário possa corrigi-las. Nomeie os testes como descrições do comportamento desejado. b. Usandovitest, escreva asserções dentro dos stubs cobrindo quaisquer valores retornados e efeitos colaterais, como lançar exceções, mutar entradas, mutar/redefinir variáveis de escopos externos, etc. Certifique-se de que os testes afirmem o comportamento desejado, capturando as suposições falhas/bugs identificados anteriormente e falhando de acordo. - Re-verifique a cobertura:
pnpm exec vitest run src/foo.test.ts --coverage - Teste de mutação apenas a função atual:
pnpm exec stryker run --mutate "src/foo.ts:<linhaInicial>-<linhaFinal>" - Revise os testes recém-escritos e repita as etapas 5a e 5b até que os critérios de teste (listados acima) sejam atendidos para a função.
- Atualize o debrief com uma seção sobre esta função.
Depois de escrever testes para todas as funções, verifique novamente se você adicionou seções ao debrief para cada função e termine de escrever a seção de resumo.
Auditando Testes
Se solicitado a auditar o projeto como um todo, verifique a cobertura de código e as pontuações de mutação em todo o projeto e recomende áreas para auditorias mais detalhadas ou para adicionar/atualizar testes. Pondere recomendações para as áreas mais importantes e propensas a bugs do código-fonte – lógica de negócios central, código orientado a eventos e funções complexas.
Se solicitado a auditar uma parte do projeto, procure por módulos ou funções (dentro do escopo do prompt) que não têm testes e relate-os ao usuário no debrief pós-auditoria.
Para cada módulo dentro do escopo do prompt que tenha testes, siga Avaliando um módulo. Em seguida, se algum critério de teste ainda não for atendido após as correções da avaliação, siga Escrevendo Testes para preencher as lacunas restantes.
Avaliando um módulo
Use este processo sempre que precisar avaliar um arquivo de teste existente em relação aos critérios de teste – tanto antes de escrever novos testes quanto ao auditar.
- Verifique a cobertura de código usando Vitest:
pnpm exec vitest run src/foo.test.ts --coverage. - Verifique mutantes sobreviventes usando Stryker Mutator:
pnpm exec stryker run --mutate "src/foo.ts". - Para cada função no módulo que tenha testes:
- Para testes baseados em propriedades:
- Procure entradas que a função aceitaria sem lançar ou retornar um valor de erro, mas que não podem ser geradas pelas arbitrariedades. Amplie as arbitrariedades conforme necessário para incluir tais entradas.
- Procure entradas que não sejam cobertas pelas arbitrariedades, especialmente entradas implícitas como campos de classe, variáveis globais, data/hora e números gerados aleatoriamente. Adicione arbitrariedades para estas.
- Corrija quaisquer erros nas implementações de mock.
- Procure gargalos de desempenho.
- Para testes baseados em propriedades:
- Registre a cobertura e as pontuações de mutação, mais quaisquer descobertas, no debrief.
Debriefs de Teste
Formate os debriefs de acordo com o modelo abaixo. Se não houver nada a relatar em uma subseção de função, simplesmente escreva "n/a". Produza o debrief em um arquivo Markdown em debriefs/.
Debrief: {nomeDoMódulo/nomeDoArquivo} {aaaa-mm-dd}
Resumo
Cobertura (branch) Antes: X% → Depois: Y% Linhas que não puderam ser cobertas: {intervalos de linhas}
Pontuação de mutação (total) Antes: X% → Depois: Y%
Problemas
{lista de marcadores de suposições falhas, bugs encontrados, problemas encontrados em testes e testes com falha. Cada item da lista deve começar com o caminho relativo à raiz do projeto para o arquivo e número(s) de linha(s) onde o problema foi encontrado}
{nomeDaFunção}
Propósito da função {uma frase descrevendo o que a função deve fazer} (confiança: alta | incerta) {se incerta: o que não está claro e por quê}
Suposições falhas encontradas
- (linha {número/intervalo de linhas}): {suposição sobre o tipo/faixa/forma da entrada que a função não valida}
Bugs encontrados na implementação
- (linha {número/intervalo de linhas}): {descrição + número da linha}
Problemas encontrados em testes pré-existentes
- (linha {número/intervalo de linhas}): {descrição do problema + nome do teste}
Casos de teste escritos
- {breve descrição de cada propriedade/invariante testada}
Testes com falha
- (linha {número/intervalo de linhas}): {nome do teste + motivo}
Mutantes imatáveis
- (linha {número/intervalo de linhas}): {breve descrição}