Não, nem todas valem o mesmo. A cobertura de linhas diz quantas instruções foram executadas; a de ramos, quantas decisões tomaram os dois caminhos; a de condições, quantas subexpressões booleanas foram testadas em verdadeiro e em falso. São três réguas distintas que costumam chegar à mesma porcentagem, mas medem coisas bem diferentes, e confundi las é a razão pela qual um "90 % de cobertura" tranquiliza uma equipe enquanto os bugs continuam passando. Este artigo separa as três, mostra com código por que a mais fácil de subir é a que menos protege, e explica qual observar quando você quer confiar no seu número.
A palavra "cobertura" soa como uma coisa só, e é aí que começa o mal entendido. É uma família de métricas de caixa branca: todas medem que parte da estrutura interna do código as suas provas tocaram, mas cada uma define "tocar" de forma mais exigente que a anterior. O padrão ISO/IEC/IEEE 29119-4 as cataloga como critérios de cobertura estrutural, e a ementa do ISTQB as trata como o termômetro de quão a fundo entrou a sua suíte [1][2]. O problema não é a métrica: é acreditar que um número alto na mais barata equivale a um software bem testado.
01 · a régua fácilCobertura de linhas: conta instruções, não lógica
A cobertura de linhas (ou de instruções) responde a uma pergunta simples: de todas as linhas executáveis do código, quantas rodou ao menos uma prova? Se você tem cem linhas e as suas provas executaram noventa, você tem 90 % de cobertura de linhas. É a métrica que quase todas as ferramentas mostram por padrão, e também a mais fácil de inflar.
O defeito é de fundo, não de calibração. Uma linha "coberta" só significa que o interpretador passou por cima dela, não que se tenha comprovado que ela faz o certo nem que os seus dois desfechos possíveis foram testados. Uma condição sem bloco else pode marcar 100 % de linhas sem nunca ter testado o que ocorre quando essa condição é falsa, porque não há nenhuma linha para executar no caso falso. A linha existiu, foi executada uma vez pelo lado verdadeiro, e a régua ficou satisfeita.
Cobrir uma linha prova que o código passou por ali. Não prova que passou por ali direito, nem que você testou o caminho que ele não tomou.
02 · a régua honestaCobertura de ramos: cada decisão, nas suas duas saídas
A cobertura de ramos (ou de decisões) eleva o nível. Já não conta linhas: conta as saídas de cada ponto de decisão. Um if tem dois ramos, verdadeiro e falso; um laço tem o ramo que entra e o que não entra; um switch tem um por caso. Para chegar a 100 % de ramos, as suas provas precisam ter feito com que cada decisão tomasse os dois caminhos ao menos uma vez.
Essa mudança de foco é exatamente o que faltava à Figura 1. A mesma função com a mesma prova única marca 100 % de linhas e só 50 % de ramos, porque o ramo "não é VIP" nunca foi executado. A cobertura de ramos obriga a acrescentar o segundo caso, e com ele aparece o cenário onde os erros de fato se escondem: o caminho que o programador deu por trivial e não testou. Por isso o ISTQB aponta que a cobertura de decisões é mais forte que a de instruções, e que 100 % da primeira garante 100 % da segunda, mas não o contrário [2].
Esses critérios estão aninhados: cobrir todos os ramos implica cobrir todas as linhas, mas não o contrário. Por isso comparar "90 % de linhas" contra "90 % de ramos" entre dois projetos é enganoso: o segundo 90 % custou mais e protege mais. Quando alguém te dá uma porcentagem de cobertura, a primeira pergunta não é quanto, mas de quê.
Ainda assim, a cobertura de ramos tem um ponto cego próprio, e é sutil. Trata cada decisão como uma caixa fechada: basta que a condição inteira resulte verdadeira uma vez e falsa outra. Não olha o que há dentro dessa condição. E quando a condição combina várias verificações com e / ou, é aí que a régua honesta ainda fica aquém.
03 · a régua finaCobertura de condições: dentro do booleano
Considere uma decisão composta: se (idade >= 18 e temPermissão). A cobertura de ramos se contenta com dois casos que tornem a expressão completa verdadeira e falsa. Você pode conseguir isso testando apenas (20, verdadeiro) e (15, falso). Em ambos os casos a variável temPermissão se moveu junto com a idade: você nunca verificou o que acontece com um maior de idade sem permissão, nem com um menor com permissão. O ramo ficou "coberto" e metade da lógica interna, sem ser tocada.
A cobertura de condições ataca justamente isso: exige que cada subexpressão booleana seja avaliada em verdadeiro e em falso separadamente. A sua variante rigorosa, a MC/DC (Modified Condition/Decision Coverage), vai além: pede demonstrar que cada condição, por si só, pode mudar o resultado da decisão completa. Não é um capricho acadêmico. A aviônica crítica o exige por norma: o padrão DO-178C obriga a MC/DC para o software de nível A, aquele cuja falha pode custar vidas [3]. Quando o bug não se paga com uma nova tentativa e sim com um acidente, a régua fina deixa de ser opcional.
04 · o número que realmente serveO que observar e o que não perseguir
Daqui sai uma conclusão incômoda para quem coleciona porcentagens: a cobertura alta é necessária mas não suficiente. 100 % de linhas convive com lógica sem testar; 100 % de ramos é muito mais difícil de fingir; a MC/DC é quase impossível sem ter pensado cada decisão. Mas nenhuma verifica se a saída é correta: medem que código foi executado, não se o resultado foi o esperado. Uma suíte pode percorrer 100 % dos ramos com asserts vazios e não pegar um único defeito.
Por isso perseguir "os 100 %" como meta costuma piorar o software: escrevem se provas de enchimento que tocam linhas sem verificar nada, só para mexer no número. A cobertura funciona ao contrário, como detector de lacunas: você a lê para achar que partes ninguém testou ainda, e decide se essa lacuna importa. Um caminho de tratamento de erro sem cobrir é um alarme; um ramo de logging sem cobrir, quase nunca. O que cobrir a fundo é o risco que define, não a vaidade da porcentagem.
A cobertura não diz se o seu software está bem testado. Diz onde com certeza não está. Esse é o seu verdadeiro poder.
Então, da próxima vez que alguém comemorar um número de cobertura, faça as duas perguntas que o desmontam: cobertura de quê, linhas ou ramos? E as lacunas que restam são de código que não importa, ou justamente do que importa? A resposta separa uma equipe que entende a sua métrica de uma que só a vê subir. Na Qirava tratamos a cobertura pelo que ela é: um mapa do que falta, não um troféu do que sobra.
Fontes
- ISO/IEC/IEEE 29119-4:2021. Software and systems engineering. Software testing. Part 4: Test techniques. International Organization for Standardization. (Define os critérios de cobertura estrutural: instrução, decisão/ramo e condição.)
- ISTQB (2018). Certified Tester Foundation Level Syllabus, v3.1. International Software Testing Qualifications Board. Seção sobre técnicas de caixa branca e cobertura de instruções e decisões.
- RTCA DO-178C / EUROCAE ED-12C (2011). Software Considerations in Airborne Systems and Equipment Certification. RTCA Inc. (Exige Modified Condition/Decision Coverage, MC/DC, para software de nível de garantia A.)
- Myers, G. J., Sandler, C. & Badgett, T. (2011). The Art of Software Testing, 3ª ed. John Wiley & Sons. Capítulo sobre provas de caixa branca e cobertura lógica.