Testes de Software: Por Que 100% de Coverage
Coverage alto e uma metrica de conforto, nao de seguranca. O Ariane 5 explodiu com codigo testado. O Knight Capital perdeu US$ 440 milhoes em 45 minutos com
Carregando
Coverage alto e uma metrica de conforto, nao de seguranca. O Ariane 5 explodiu com codigo testado. O Knight Capital perdeu US$ 440 milhoes em 45 minutos com
O time tinha orgulho. 97% de cobertura de testes. CI passando verde em todos os commits. E entao o sistema falhou em producao de uma forma que nenhum dos testes testava. A historia se repete em empresas de todos os tamanhos, desde startups ate organizacoes com centenas de engenheiros.
Coverage mede quantas linhas de codigo foram executadas pelos seus testes. Nao mede se os comportamentos corretos foram verificados. Essa distincao parece sutil mas e a diferenca entre um sistema que voce pode confiar e um que passa no CI e falha em producao. Antes de entrar nos casos historicos, o <a href='/2025-3/a-historia-nao-contada-do-desa'>desastre do Ariane 5</a> e um estudo de caso completo sobre como testes insuficientes destroem sistemas criticos.
Imagine esse teste: voce tem uma funcao que divide dois numeros. Seu teste chama divisao(10, 2) e verifica que retorna 5. Coverage: 100%. Bug em producao quando alguem chama divisao(10, 0): divisao por zero, excecao nao tratada. O teste existia. O coverage era perfeito. O bug tambem.
Coverage mede caminhos executados, nao hipoteses verificadas. Voce pode executar uma linha de codigo e nao verificar o que ela retorna. Voce pode testar apenas o caminho feliz e deixar todos os casos de erro sem cobertura real. Coverage alto significa que o codigo foi executado — nao que ele esta correto.
Testes de Software: Por Que 100% de Coverage. Coverage alto e uma metrica de conforto, nao de seguranca. O Ariane 5 explodiu com codigo testado. O Knight Capital perdeu US$ 440 milhoes em 45 minutos com sistema que passava em todos os testes. Entenda o que realmente pega os bugs que importam.
O Ariane 5 explodiu 37 segundos apos o lancamento em 1996. O bug era uma conversao de numero de ponto flutuante de 64 bits para inteiro de 16 bits que resultava em overflow. O codigo vinha do Ariane 4 — e tinha sido testado exaustivamente... para o Ariane 4.
O problema nao foi falta de testes. Foi que os testes testavam o comportamento do sistema anterior com a trajetoria do sistema anterior. O Ariane 5 tinha velocidade horizontal muito maior na decolagem, o que nunca havia sido parte dos casos de teste. Os testes passavam. O foguete explodiu. Uma peca de codigo com cobertura de 100% em seu contexto original causou o desastre em um contexto novo.
Em agosto de 2012, a Knight Capital perdeu US$ 440 milhoes em 45 minutos. Um codigo legado de uma funcao de trading que havia sido desabilitada tinha sido reativado acidentalmente durante um deploy. O sistema passou em todos os testes — porque os testes testavam o comportamento intencional, nao o comportamento quando codigo antigo e ativado por engano.
O que nao existia: teste de rollback automatizado, teste do estado do sistema apos deploy, monitoramento que detectasse volume de ordens absurdo e interrompesse automaticamente. Os testes de unidade passavam. O sistema destruiu a empresa.
Testes cobriam comportamento isolado, nao comportamento do sistema como um todo
Cenarios de falha e recuperacao nao foram testados
Mudancas de contexto (novo hardware, novo volume, novo deploy) invalidaram suposicoes dos testes
Ausencia de testes de limites e casos extremos de valores de entrada
Em vez de escrever casos de teste especificos, voce define propriedades que devem ser verdadeiras para qualquer entrada valida. A ferramenta gera centenas de casos automaticamente, incluindo casos extremos que voce nunca pensaria em escrever.
Exemplo: em vez de testar serialize(deserialize('hello')) === 'hello', voce define a propriedade: para qualquer string valida X, serialize(deserialize(X)) deve retornar X. A ferramenta testa com strings vazias, com caracteres especiais, com Unicode, com strings muito longas. Ela vai encontrar casos que voce nunca escreveria manualmente.
A piramide de testes tradicional (muitos unitarios, poucos de integracao) faz sentido em teoria. Na pratica, a maioria dos bugs que chegam em producao nao esta em logica de unidade — esta na comunicacao entre componentes. Banco de dados, servicos externos, filas de mensagem.
Uma suite com 60% de coverage em testes de integracao com banco de dados real (mesmo que em container Docker) vai pegar mais bugs relevantes do que 95% de coverage em testes unitarios com tudo mockado.
Chaos engineering e a pratica de introduzir falhas propositalmente em sistemas em producao (ou pre-producao) para verificar como eles se comportam. O Netflix criou o Chaos Monkey — um servico que termina instancias aleatoriamente em producao para garantir que o sistema sobrevive a falhas de infraestrutura.
Para a maioria dos times, chaos engineering começa mais modesto: matar o banco de dados durante um teste e verificar se o sistema falha graciosamente, cortar a conexao com um servico externo e ver se o timeout esta configurado corretamente.
O sistema funciona com 10 usuarios. Sera que funciona com 1.000 simultaneos? Testes de carga verificam o comportamento sob volume esperado. Testes de stress empurram alem do limite para descobrir onde o sistema quebra — antes que o usuario descubra.
Se coverage nao e a metrica certa, quais sao? Aqui estao algumas que dao uma visao mais honesta da qualidade do seu suite de testes.
Times pequenos nao tem recursos para chaos engineering elaborado e suites de testes de todos os tipos. O pulo do gato esta em alocar o esforco de teste onde o risco e maior.
Testes de integracao para o caminho critico do negocio: o que acontece se isso quebrar?
Testes de regressao para cada bug que chegou em producao — nunca deixar repetir
Testes de carga simples antes de features que podem ter pico de acesso
Smoke tests automatizados pos-deploy: as 5 coisas mais importantes funcionam?
Property-based testing para funcoes que processam dados externos de usuario
E se voce so puder fazer uma coisa: escreva um teste para cada bug que checar em producao, antes de corrigir. Isso cria automaticamente um historico de testes de regressao para os problemas reais que o seu sistema teve. E muito mais valioso do que perseguir 100% de coverage em codigo trivial.
Para garantir que problemas nao escapam antes de chegar nos testes, practices de <a href='/2026/code-review-boas-praticas-software-critico'>boas praticas de code review</a> sao o complemento natural de uma boa suite de testes.
No fundo, a obsessao com coverage e um sintoma de um problema cultural: as organizacoes querem uma metrica simples para dizer 'estamos seguros'. Coverage e facil de medir, facil de reportar e facil de gamificar — e por isso vira meta.
A Lei de Goodhart se aplica aqui: quando uma metrica se torna um objetivo, ela deixa de ser uma boa metrica. Times que precisam atingir 80% de coverage vao escrever testes que executam codigo sem verificar comportamento, exatamente para subir o numero. O coverage vai a 80%. A qualidade real nao muda.
A mudanca real e cultural: testes existem para dar confianca, nao para satisfazer uma metrica. Um teste que nao te da confianca para fazer um refactor nao esta fazendo o trabalho dele, independente do que o coverage report diz.
100% de coverage e uma conquista vazia se os testes nao verificam os comportamentos corretos, nao testam os caminhos de erro, e nao capturam o comportamento do sistema como um todo. O Ariane 5 e o Knight Capital falaram mais sobre isso do que qualquer artigo academico.
A pergunta certa nao e 'qual e o nosso coverage?'. E: 'quao confiante eu fico para fazer mudancas depois de ver os testes passando?'. Se a resposta for 'bem confiante', voce tem uma boa suite. Se a resposta for 'com medo mesmo assim', coverage nao vai resolver isso.
Bugs que custaram bilhões