StrykerJS é uma ferramenta de testes de mutação para JavaScript e TypeScript. Em vez de medir apenas quais linhas foram executadas, ela modifica pequenas partes do código e verifica se os testes detectam o comportamento incorreto. Se uma mutação sobrevive, existe uma lacuna na suíte, uma asserção fraca ou código sem efeito observável.
Exemplo: o código usa > e Stryker troca por >=. Se todos os testes continuam passando, talvez o caso de limite nunca tenha sido validado. Mutation testing responde à pergunta: os testes realmente impedem regressões?
Por que cobertura não basta
Uma linha pode ser executada sem que o resultado seja verificado:
export function desconto(valor, vip) {
if (vip) return valor * 0.8;
return valor;
}Um teste que apenas chama desconto(100, true) cobre a linha, mas não garante que o desconto seja 20%. Stryker pode alterar 0.8 para outro valor; a mutação sobreviverá se não houver asserção.
Instalação
npm install -D @stryker-mutator/core
npx stryker initO assistente pergunta framework de teste, reporter e configuração. Para Node Test Runner, Jest, Vitest, Mocha e outros, instale o runner correspondente quando necessário.
Configuração inicial
Crie stryker.config.mjs:
export default {
mutate: [
'src/**/*.js',
'!src/**/*.test.js',
'!src/generated/**',
],
testRunner: 'command',
commandRunner: {
command: 'npm test',
},
reporters: ['clear-text', 'progress', 'html'],
coverageAnalysis: 'perTest',
thresholds: {
high: 80,
low: 60,
break: 55,
},
};O runner command é genérico. Integrações específicas costumam ser mais rápidas porque permitem selecionar testes relacionados.
Executando
npx stryker runO processo executa primeiro os testes sem mutação. Depois cria mutantes, roda testes e classifica os resultados.
Status dos mutantes
- Killed: um teste falhou e matou a mutação;
- Survived: testes passaram; exige investigação;
- No coverage: nenhum teste executou o mutante;
- Timeout: mutação causou execução lenta ou infinita;
- Compile error: mutação gerou código inválido;
- Ignored: mutante excluído por configuração;
- Runtime error: processo falhou antes de uma asserção útil.
Mutation score
A pontuação geralmente considera mutantes mortos sobre mutantes válidos. Uma porcentagem alta sugere testes sensíveis, mas não é objetivo absoluto. Código gerado, defensivo ou impossível de observar pode produzir mutantes equivalentes.
Use o score como indicador e tendência. Não escreva testes inúteis apenas para alcançar 100%.
Mutadores comuns
Stryker altera:
- operadores aritméticos;
- comparações;
- booleanos;
- condições;
- strings e números;
- retornos;
- expressões opcionais;
- blocos e chamadas.
Cada alteração representa uma possível falha introduzida por um desenvolvedor.
Exemplo de mutante sobrevivente
export function podeComprar(saldo, preco) {
return saldo >= preco;
}Teste insuficiente:
test('permite quando há saldo', () => {
assert.equal(podeComprar(100, 50), true);
});Se >= for trocado por >, o teste passa. Adicione limite:
test('permite quando saldo é igual ao preço', () => {
assert.equal(podeComprar(50, 50), true);
});Mutantes equivalentes
Algumas mudanças não alteram comportamento observável. Exemplo: uma condição redundante ou valor que nunca chega ao consumidor. Esses mutantes não podem ser mortos por um teste legítimo.
Antes de adicionar um teste, confirme se a mutação realmente muda a saída, efeito ou erro público. Caso seja equivalente, ignore de forma documentada ou refatore o código.
Selecionando arquivos
Não comece pelo repositório inteiro. Escolha módulos críticos:
mutate: [
'src/domain/**/*.ts',
'src/security/**/*.ts',
'!src/**/*.d.ts',
]Priorize regras de negócio, cálculo, autorização e transformações de dados. Controllers simples e arquivos gerados podem ter menor retorno.
Performance
Mutation testing executa muitos testes. Para controlar duração:
- use runner específico;
- ative análise por teste;
- mutacione somente arquivos alterados ou críticos;
- configure workers paralelos;
- elimine testes lentos;
- use execução incremental;
- rode suíte completa em agenda noturna.
Workers
Configure concorrência:
concurrency: 4Mais workers aumentam CPU e memória. Em CI compartilhado, excesso pode tornar tudo mais lento.
Timeouts
Uma mutação pode remover condição de parada. Stryker usa fator e margem sobre a duração normal:
timeoutFactor: 1.5,
timeoutMS: 5000Não aumente indiscriminadamente. Timeouts frequentes podem indicar testes instáveis ou mutações em loops sensíveis.
TypeScript
Stryker suporta TypeScript por transformação e plugins. Mantenha typecheck separado:
npm run typecheck
npx stryker runTestes de mutação não substituem o compilador. Algumas mutações podem violar tipos antes de executar; configure o checker quando necessário.
Jest ou Vitest
Use plugin correspondente para aproveitar seleção e cobertura:
npm install -D @stryker-mutator/jest-runner
export default {
testRunner: 'jest',
jest: {
projectType: 'custom',
configFile: 'jest.config.js',
},
};Para Vitest, use o runner mantido e alinhe a versão suportada.
Node Test Runner
Quando não houver integração específica adequada, use command runner. Organize testes para que o comando seja determinístico e termine corretamente.
Relatório HTML
O reporter HTML mostra arquivo, linha, mutação e testes executados. Publique o relatório como artefato do CI, sem expor código privado em locais públicos.
Thresholds
thresholds: {
high: 85,
low: 70,
break: 65,
}break faz o processo falhar abaixo do limite. Comece com o nível atual e aumente gradualmente. Um limite impossível incentiva ignores indevidos.
Baseline e incremental
Recursos incrementais evitam reprocessar mutantes cujo código e testes não mudaram. Armazene resultados entre execuções conforme a configuração e invalide o cache quando versões, config ou ambiente mudarem.
CI em pull requests
Uma estratégia prática:
- unit tests em todos os PRs;
- mutação somente nos arquivos alterados;
- relatório completo em branch principal;
- execução completa noturna;
- tendência registrada em dashboard.
Stryker Dashboard
O dashboard armazena mutation score por branch e commit. Proteja token e avalie política de envio de dados para projetos privados.
Desabilitando mutantes
É possível ignorar trecho específico, mas documente a razão:
// Stryker disable next-line StringLiteral: mensagem não faz parte do contrato
throw new Error('Falha interna');Não use comentários para esconder testes fracos. Revise ignores periodicamente.
Testes de integração
Mutacionar código que inicia bancos e containers pode ser muito caro. Separe lógica de domínio para testes rápidos e mantenha poucos mutantes em camadas integradas.
Interpretação correta
Um mutante sobrevivente pode indicar:
- falta de teste;
- asserção insuficiente;
- código morto;
- comportamento não observável;
- mutante equivalente;
- teste selecionado incorretamente.
A ação não é sempre “adicionar teste”. Às vezes é remover ou simplificar código.
Erros comuns
- rodar todo o monorepo no primeiro dia;
- buscar 100% sem análise;
- ignorar muitos mutantes;
- usar testes lentos e instáveis;
- não fixar versões de plugins;
- confundir cobertura com mutation score;
- não revisar mutantes equivalentes.
Fluxo recomendado
Comece por um módulo crítico, estabeleça baseline, analise sobreviventes, fortaleça asserções e expanda gradualmente. Combine com Node Test Runner, integração real em Testcontainers, contratos em Pact no Node.js e CI em GitHub Actions.
Consulte a documentação oficial do StrykerJS e a lista oficial de mutadores suportados.


