Home > Blog > Desenvolvimento Web
Desenvolvimento Web
JavaScript
Programação

Cobertura de Testes no Node.js

Atualizado em: 17 de agosto de 2026

Estação de trabalho usada no desenvolvimento de aplicações Node.js

A Cobertura de Testes no Node.js mede quais linhas, funções e branches foram executados durante a suíte. O Node.js Test Runner consegue coletar cobertura usando o V8, gerar resumo no terminal e produzir arquivo LCOV para integração com ferramentas de CI.

Na documentação do Node.js 26.5.0, a coleta nativa continua marcada como experimental. Isso significa que flags, formato e comportamento podem mudar. O recurso já é útil em projetos que fixam a versão do runtime e validam a saída no pipeline.

Neste guia, você aprenderá a ativar cobertura, configurar include e exclude, gerar LCOV, definir thresholds no CI, interpretar métricas, ignorar código justificadamente, combinar testes unitários e integração e evitar a busca por 100% sem qualidade.

O que é cobertura de código?

Cobertura registra quais partes do programa foram executadas pelos testes. A documentação oficial de cobertura do Node.js Test Runner descreve a flag e os filtros. A documentação de cobertura do V8 explica a coleta nativa do motor.

Para criar a suíte, consulte Node Test Runner. Para dependências simuladas, veja Mocks no Node.js Test Runner. O artigo de Módulo V8 no Node.js ajuda a entender o motor.

Ativando cobertura

node --test --experimental-test-coverage

Ao final, reporters como spec e tap mostram um resumo. Como a flag é experimental, fixe a versão usada pelo projeto.

Script no package.json

{
  "scripts": {
    "test": "node --test",
    "test:coverage": "node --test --experimental-test-coverage"
  }
}

Métricas principais

  • Lines: linhas executadas.
  • Functions: funções chamadas.
  • Branches: caminhos condicionais executados.

Uma linha coberta não prova que o resultado foi validado. O teste pode executar o código sem fazer assert útil.

Incluindo arquivos

node --test \
  --experimental-test-coverage \
  --test-coverage-include='src/**/*.js'

O filtro permite incluir arquivos que talvez não tenham sido carregados. Coloque globs entre aspas para evitar expansão diferente pelo shell.

Excluindo arquivos

node --test \
  --experimental-test-coverage \
  --test-coverage-exclude='src/generated/**'

Exclua código gerado, migrations imutáveis ou arquivos sem lógica apenas quando houver justificativa.

Testes e node_modules

Por padrão, módulos internos do Node.js, node_modules e arquivos de teste costumam não entrar no relatório. As opções de inclusão e exclusão podem alterar esse comportamento.

Gerando LCOV

node --test \
  --experimental-test-coverage \
  --test-reporter=lcov \
  --test-reporter-destination=lcov.info

O reporter LCOV não mostra necessariamente o resultado normal dos testes. Use múltiplos reporters quando precisar de saída humana e arquivo:

node --test \
  --experimental-test-coverage \
  --test-reporter=spec \
  --test-reporter-destination=stdout \
  --test-reporter=lcov \
  --test-reporter-destination=lcov.info

Diretório coverage

mkdir -p coverage
node --test \
  --experimental-test-coverage \
  --test-reporter=lcov \
  --test-reporter-destination=coverage/lcov.info

Adicione artefatos gerados ao .gitignore, salvo quando a política do projeto exige versioná-los.

NODE_V8_COVERAGE

NODE_V8_COVERAGE=coverage/raw node --test

A variável solicita arquivos brutos de cobertura do V8. Eles podem ser grandes e precisar de processamento adicional.

Cobertura de TypeScript

Quando o Node.js executa TypeScript com remoção de tipos ou quando o projeto transpila código, confirme se os caminhos e source maps correspondem à fonte. Caso contrário, a cobertura pode apontar para JavaScript gerado.

Consulte o que é TypeScript e Module API no Node.js para source maps.

Branches

function calculateDiscount(user) {
  if (user.isPremium) {
    return 0.2;
  }

  return 0;
}

Um teste apenas com usuário premium cobre parte das linhas, mas deixa o branch comum sem validação.

Condições compostas

if (user.active && user.age >= 18) {
  allowAccess();
}

Teste combinações relevantes: ativo e adulto, inativo e adulto, ativo e menor. Não é necessário cobrir combinações impossíveis pelo domínio.

Funções não chamadas

Functions coverage revela código que nenhum teste invoca. Pode indicar lacuna, função morta ou export desnecessário.

Cobertura não é qualidade

Este teste aumenta cobertura sem verificar comportamento:

test('executa função', () => {
  calculateTotal([{ price: 10 }]);
});

Prefira:

test('soma os preços', () => {
  assert.equal(
    calculateTotal([{ price: 10 }, { price: 15 }]),
    25
  );
});

Thresholds

O runner nativo pode não oferecer em todas as versões a mesma configuração de thresholds encontrada em ferramentas externas. Uma opção é analisar o LCOV ou o evento de cobertura em um script de CI.

Threshold global

Exigir um percentual mínimo global evita regressões grandes, mas pode esconder um módulo crítico sem testes porque outros arquivos têm cobertura alta.

Threshold por arquivo

Aplicar limites por arquivo é mais rigoroso. Exclua adapters triviais apenas com revisão.

Regra de não regressão

Uma política prática é impedir que a cobertura total diminua e exigir cobertura alta para código novo.

Diff coverage

Ferramentas externas podem calcular cobertura apenas das linhas alteradas. Isso ajuda projetos legados a melhorar gradualmente.

CI com GitHub Actions

- name: Run tests with coverage
  run: npm run test:coverage

- name: Upload coverage
  uses: actions/upload-artifact@v4
  with:
    name: coverage
    path: coverage/lcov.info

Fixe a versão do Node.js no workflow.

Falha dos testes

Cobertura só faz sentido quando a suíte passa. Não publique relatório de uma execução interrompida como se fosse completo.

Processos filhos

O Test Runner executa arquivos em processos separados por padrão. A infraestrutura do runner combina a cobertura. Processos criados manualmente pela aplicação podem exigir configuração adicional.

Worker Threads

Confirme se código executado em workers aparece conforme esperado. Testes específicos do worker ajudam a evitar lacunas.

Veja Worker Threads no Node.js.

Código carregado dinamicamente

Imports condicionais só aparecem quando o caminho é executado. Crie testes para plugins e configurações relevantes.

Erros e catch

try {
  return await repository.find(id);
} catch (error) {
  logger.error(error);
  throw new ServiceError('Falha ao buscar');
}

Teste também a rejeição do repositório para cobrir o catch e validar o erro traduzido.

Retries

Teste sucesso inicial, falha transitória seguida de sucesso e esgotamento. Veja Retry com Backoff no Node.js.

Código de shutdown

Handlers de sinais são difíceis de testar no mesmo processo. Extraia a lógica de shutdown para uma função e teste com dependências falsas.

Ignorando linhas

O Node.js reconhece comentários de cobertura:

/* node:coverage ignore next */
if (platformSpecificCondition) {
  runPlatformFallback();
}

Ignorando várias linhas

/* node:coverage ignore next 3 */
if (impossibleInProduction) {
  emergencyFallback();
}

Disable e enable

/* node:coverage disable */
function generatedCode() {
  // conteúdo gerado
}
/* node:coverage enable */

Comentários de ignore devem ser raros e revisados. Eles podem esconder código testável.

Código defensivo

Não ignore automaticamente branches “impossíveis”. Se protegem contra entrada inválida, teste a falha.

Código gerado

Prefira excluir pela configuração em vez de inserir comentários em arquivos gerados.

Ordenação de testes

Uma suíte que depende de ordem pode produzir cobertura variável. Isole estado e use diretórios, bancos e portas independentes.

Randomização

Versões recentes do Test Runner oferecem randomização experimental ou em desenvolvimento. Isso ajuda a encontrar dependência de ordem, mas não funciona em todas as estruturas de subtests.

Watch mode

Coverage em watch pode gerar resultados parciais e custo alto. Para relatório oficial, execute a suíte completa em processo limpo.

Snapshots

Snapshots aumentam cobertura ao executar serializers, mas não substituem asserts de regras críticas.

Mocks

Mocks podem cobrir branches de erro rapidamente, porém escondem problemas de integração. Equilibre com testes reais.

Testes unitários

São rápidos e cobrem regras isoladas. Normalmente compõem a maior parte da suíte.

Testes de integração

Cobrem banco, filesystem, filas e HTTP reais ou em containers. São mais lentos, mas validam contratos.

Testes end-to-end

Cobrem caminhos completos. Mesmo com poucos casos, protegem fluxos críticos.

Arquivos sem cobertura

Se um arquivo nunca é importado, ele pode não aparecer sem um include explícito. Configure o glob de source para revelar arquivos completamente não testados.

Arquivos de entrada

Scripts CLI, server bootstrap e migrations costumam ter baixa cobertura. Extraia lógica para módulos testáveis e mantenha o entrypoint pequeno.

Relatórios HTML

O reporter nativo gera LCOV, que pode ser convertido por ferramentas externas em HTML navegável. Não exponha o relatório publicamente se caminhos ou código forem sensíveis.

Armazenamento de artefatos

Retenha relatórios por período limitado. Arquivos brutos do V8 podem consumir espaço significativo.

Performance

Coletar cobertura adiciona overhead. Não use em benchmarks de desempenho e não habilite permanentemente em produção.

Flakiness

Testes instáveis tornam o percentual variável. Corrija flakiness antes de usar cobertura como gate rígido.

Revisão humana

Um relatório mostra “onde não passou”, não “quais casos faltam”. A revisão deve perguntar:

  • quais regras têm maior risco;
  • quais erros não foram simulados;
  • quais integrações não foram exercitadas;
  • quais asserts são fracos;
  • quais testes dependem de implementação.

Meta realista

Projetos diferentes precisam de metas diferentes. Código financeiro e autorização merece cobertura e revisão maiores que adapters simples.

Testes de mutação

Mutation testing altera o código e verifica se os testes detectam a mudança. Ele mede força dos asserts melhor que cobertura isolada, mas custa mais.

Erros comuns

  • Buscar 100% cegamente: testes sem valor são criados.
  • Não incluir todo src: arquivos sem teste desaparecem.
  • Ignorar branches: caminhos de erro ficam frágeis.
  • Usar cobertura experimental sem fixar Node: CI muda.
  • Publicar relatório de suíte falha: dados ficam incompletos.
  • Habilitar em benchmark: resultados são distorcidos.
  • Confiar apenas em unitários: integrações quebram.

Boas práticas

  • Fixe a versão do Node.js.
  • Inclua todos os fontes.
  • Exclua apenas com justificativa.
  • Gere LCOV no CI.
  • Evite regressão.
  • Revise branches críticos.
  • Combine níveis de teste.
  • Use ignores raramente.
  • Corrija testes instáveis.
  • Avalie qualidade dos asserts.

Conclusão

A Cobertura de Testes no Node.js oferece visibilidade sobre linhas, funções e branches executados pelo Test Runner, com filtros e saída LCOV.

Como a coleta permanece experimental no Node.js 26.5.0, o projeto deve fixar o runtime e validar a integração. O percentual é um indicador, não o objetivo final. Com includes completos, thresholds equilibrados e testes de integração, a cobertura ajuda a localizar riscos sem incentivar uma falsa sensação de segurança.

Os 10 Melhores Cursos de Programação de 2026

Descubra os melhores cursos de programação. Aprenda a escolher o curso ideal para iniciar ou avançar na carreira de desenvolvedor

POSTS RELACIONADOS

Ver todos

Seta para a direita