Code Coverage: 90% pode não significar absolutamente nada

Uma meta de 90% de cobertura de código parece um objetivo razoável até alguém perceber que dá para chegar em 90%, ou até 100%, sem verificar praticamente nada de útil sobre o comportamento do sistema. A métrica mede execução de linha, não qualidade de verificação, e tratar as duas coisas como sinônimo é onde o problema começa.

O que a métrica realmente conta

Code coverage mede a proporção de código que foi executada durante a rodada de testes, tipicamente reportada como line coverage (percentual de linhas executadas), branch coverage (percentual de caminhos lógicos, como cada ramo de um if ou switch, efetivamente percorridos) ou coverage por método. Nenhuma dessas variações mede se o teste, ao executar aquela linha, verificou algo relevante sobre o resultado produzido.

[Fact]
public void CalcularDesconto_Executa()
{
    var resultado = calculadora.CalcularDesconto(100, 0.1m);
    // sem nenhuma asserção sobre o valor de "resultado"
}

Esse teste conta como cobertura de 100% do método CalcularDesconto. Ele também não prova absolutamente nada sobre se o cálculo está correto, porque nunca verifica o valor retornado contra o que seria esperado.

Branch coverage é mais rigoroso, e ainda insuficiente sozinho

Line coverage esconde um problema óbvio: uma linha só de if (condicao) pode ser marcada como coberta mesmo que só um dos dois caminhos (verdadeiro ou falso) tenha sido de fato exercitado pelos testes. Branch coverage corrige parcialmente isso, exigindo que ambos os caminhos lógicos sejam percorridos para contar como totalmente coberto.

Ainda assim, mesmo com 100% de branch coverage, cada caminho pode ter sido percorrido por um teste sem asserção relevante sobre o resultado daquele caminho específico. O caminho foi executado. Ninguém verificou se o comportamento naquele caminho estava correto.

Cobertura alta pode significar teste ruim, não sistema seguro

Isso cria um incentivo perverso quando cobertura vira meta de gestão isolada: é mais fácil escrever um teste que só chama o método (inflando o número) do que escrever um teste que realmente verifica comportamento relevante, incluindo casos de borda. Uma equipe pressionada a bater uma meta numérica de cobertura, sem revisão sobre a qualidade das asserções, tem incentivo real para produzir exatamente o tipo de teste que não protege contra nada, só para satisfazer o painel de métrica.

O resultado é uma suíte de testes que passa 100% verde, reporta 90% de cobertura, e ainda assim deixa passar um bug óbvio para produção, porque a linha onde o bug vivia estava “coberta” por um teste que nunca checou se o resultado fazia sentido.

Ferramentas ajudam a medir, não a garantir qualidade

Coverlet é a ferramenta de referência no ecossistema .NET para medir cobertura, cross-platform, com suporte a line, branch e method coverage, exportando em formatos como cobertura ou opencover para integração com relatório de CI. Para quem trabalha no Visual Studio no Windows, a extensão Fine Code Coverage mostra essa informação diretamente no editor, destacando visualmente quais linhas foram exercitadas pelos testes.

Nenhuma dessas ferramentas resolve o problema descrito acima, porque nenhuma delas avalia a qualidade da asserção dentro do teste. Elas medem execução, com precisão e utilidade real para identificar código completamente não testado. Só não deveriam ser lidas como medida de confiabilidade do sistema.

Cobertura é sinal de ausência, não prova de presença

O uso mais honesto de code coverage é negativo: um número baixo indica com segurança que existe código não exercitado por nenhum teste, o que é informação útil e acionável. Um número alto não indica, com a mesma segurança, que o sistema está bem testado, porque a métrica não enxerga a diferença entre um teste que verifica comportamento de verdade e um teste que só passa pela linha sem checar nada relevante depois.

Tratar 90% de cobertura como prova de qualidade é confundir “alguém passou por aqui” com “alguém verificou que isso funciona direito”. São afirmações bem diferentes, e só a segunda realmente importa.