Introdução
Como já sabemos, o teste caixa-branca deriva casos a partir da estrutura interna do código, e que cobertura mede isso. Mas a “cobertura” não é um número único, são pelo menos três métricas diferentes, que respondem perguntas diferentes.
| Tipo | Pergunta que responde | Granularidade |
|---|---|---|
| Statement/Line coverage | Essa linha foi executada ao menos uma vez? | A mais grosseira |
| Branch/Decision coverage | Cada desvio condicional (if/else, switch, operador ternário) foi exercitado nos dois sentidos? | Intermediária |
| Method/Function coverage | Esse método foi chamado ao menos uma vez? | A mais grosseira de todas |
Isso importa porque 100% de line coverage não significa 100% de branch coverage. Exemplo:
public bool PodeContratar(int idade, bool temCpfValido)
{
return idade >= 18 && temCpfValido;
}
Com um único teste — PodeContratar(20, true) — a linha do método roda inteira: line coverage = 100%. Mas a expressão booleana só tomou o caminho "verdadeiro"; o caminho "falso" (disparado por idade < 18 ou CPF inválido) nunca foi exercitado. Branch coverage real aqui é 50% — metade dos desvios possíveis daquela decisão nunca rodou, mesmo com a linha 100% coberta.
É por isso que ferramentas sérias de cobertura, como o Coverlet, reportam line e branch separadamente e o segundo número costuma ser mais honesto sobre o que realmente foi testado.
Por que % de cobertura sozinha não prova qualidade
Cobertura é sinalizador de ausência ("essa linha nunca rodou"), não prova de correção ("essa linha está certa"). Um teste pode exercitar 100% das linhas de um método sem afirmar nada sobre o resultado:
[Fact]
public void CalcularDesconto_NaoEstoura()
{
var resultado = service.CalcularDesconto(100, true);
// nenhum Assert sobre o valor retornado — só "não jogou exceção"
}
Essa linha conta como "coberta" pra qualquer ferramenta de cobertura, mesmo que o teste nunca verifique se o valor calculado está certo. Esse é exatamente o "teatro de métricas" que vimos no Módulo 1: cobrar % de cobertura como meta isolada incentiva esse tipo de teste vazio — ele sobe o número sem provar nada.
A forma honesta de saber se os testes realmente provam algo é o mutation testing (Módulo 13 do roteiro): a ferramenta injeta bugs artificiais no código (troca > por >=, troca && por ||, inverte uma condição) e verifica se algum teste falha. Se nenhum falhar, aquele trecho está "coberto" mas não está "testado" de verdade. Cobertura e mutation score respondem perguntas diferentes e complementares — cobertura diz o quê roda, mutation score diz o quê prova algo.
Práticas de mercado
Não existe um número universal, mas um padrão dominante.
"Qual % é o padrão do mercado" tem uma resposta mais sutil que um número fixo. A prática que mais se consolidou nos últimos anos não é "todo o repositório precisa de X%" — é medir cobertura só no código novo ("coverage on new code" / "diff coverage" / "patch coverage").
| Ferramenta | Como chama o conceito | Valor default comum |
|---|---|---|
| SonarQube/SonarCloud (quality gate "Sonar Way") | Coverage on New Code | ≥ 80% |
| Codecov | Patch coverage | 70–80% (configurável) |
| Coveralls | Coverage diff | configurável, sem default fixo |
Por que isso virou o padrão dominante em vez de "% do repositório inteiro": um arquivo legado com 20% de cobertura não deveria travar um fix de 2 linhas feito hoje, mas essas 2 linhas deveriam estar cobertas. Cobrar % total do repositório pune código antigo que ninguém vai reescrever, e isso empurra o time a burlar a métrica (excluir arquivos do cálculo, escrever teste vazio só pra subir número). Cobrar só o código que mudou é objetivamente mais justo e mais difícil de contornar.
- Código novo em geral: 70–80% é o número mais citado como "saudável" por times maduros — nem 0% (sem rede de segurança), nem 100% (raramente vale o custo marginal).
- Código crítico/regulado (cálculo fiscal, financeiro, de saúde): times costumam exigir mais — 90%+ de linha, combinado com mutation score mais alto, porque o custo de um defeito escapar é desproporcional (a e-Financeira tem fiscalização da Receita, como vimos no Módulo 1).
- Código de "cola" (DI, mapeamento simples, controllers finos): cobertura alta aqui tem retorno marginal baixo — times maduros não forçam o mesmo número.
- Mutation score, quando adotado, normalmente é configurado abaixo do número de cobertura de linha (ex.: cobertura 80% + mutation score 60%), porque matar mutante é um critério mais rígido — cobrar o mesmo número nos dois é irrealista.
Top comments (0)