Testes de Mutação em Rust: como encontrar o que até 100% dos testes podem perder

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

O paradoxo da cobertura de 100% — ou por que seus testes podem estar mentindo

\n\n

Você escreveu um código ideal em Rust. O compilador está satisfeito, o clippy está em silêncio, os testes passam em uma onda verde, e o relatório de cobertura informa orgulhosamente: 100% das linhas estão cobertas. Pode respirar aliviado? Não. Testes de mutação em Rust é exatamente a técnica que mostrará que seus testes, talvez, protegem o ar, e não a lógica.

\n\n

Imagine: você acidentalmente muda + para - em uma função crítica. Os testes ainda estão verdes? Parabéns, você encontrou um mutante — um defeito que vive no seu código, mas que os testes não percebem. Neste artigo, vamos analisar como testes de mutação em Rust ajudam a encontrar esses pontos cegos e implementá-lo em um projeto real.

\n\n

O que são testes de mutação (e por que Rust é ideal para eles)

\n\n

Testes de mutação (ou teste de mutação) é um método para avaliar a qualidade dos testes, no qual pequenas alterações artificiais (mutações) são introduzidas no código-fonte e, em seguida, os testes são executados. Se os testes não falharem no código mutado — significa que o mutante sobreviveu. Quanto mais mutantes sobreviventes, piores são seus testes.

\n\n

Rust é uma linguagem que, por si só, detecta classes inteiras de erros em tempo de compilação: condições de corrida, ponteiros nulos, use-after-free. Mas testes de mutação em Rust visam especificamente erros lógicos — aquilo que o compilador não vê. E isso torna Rust uma plataforma ideal: você já está protegido contra 70% dos bugs típicos, e os testes de mutação eliminam os 30% restantes.

\n\n

Como é uma mutação no código Rust

\n\n

Mutações típicas incluem:

\n
    \n
  • Alteração de operadores: a + b → a - b
  • \n
  • Inversão de condições: if x > 5 → if x < 5
  • \n
  • Substituição de constantes: let n = 42 → let n = 0
  • \n
  • Remoção de chamadas de função: process(data) → chamada vazia
  • \n
  • Alteração de limites de iteração: 0..n → 0..=n
  • \n
\n\n

Se após essa mutação pelo menos um teste não falhar — significa que você perdeu tempo escrevendo esse teste. Ele não verifica o que deveria.

\n\n

Ferramentas para testes de mutação em Rust: cargo-mutants

\n\n

Atualmente, a principal ferramenta para testes de mutação em Rust é o cargo-mutants. Ele é maduro, ativamente mantido e fornece relatórios legíveis. A instalação é simples:

\n\n
cargo install cargo-mutants\n
\n\n

Depois disso, basta executar:

\n\n
cargo mutants\n
\n\n

E a ferramenta encontrará automaticamente todas as mutações no seu código, executará os testes e gerará um relatório HTML com indicação por cores: verde — mutantes mortos (testes funcionaram), vermelho — sobreviventes (problema).

\n\n

Alternativas no radar

\n
    \n
  • mutagen — mais antigo, mas ainda funcional. Menos mutações, mais rápido em projetos pequenos.
  • \n
  • cargo-insta — não é exatamente teste de mutação, mas é próximo: ajuda a verificar visualmente alterações em snapshots de teste.
  • \n
\n\n

Para trabalho sério, recomendo o cargo-mutants — ele oferece o melhor equilíbrio entre velocidade e profundidade de análise.

\n\n

Exemplo prático: testando uma calculadora de descontos

\n\n

Suponha que temos um módulo para calcular descontos em uma loja online em Rust:

\n\n
// src/discount.rs\n\npub fn calculate_discount(price: f64, customer_age: u32) -> f64 {\n    let mut discount = 0.0;\n\n    // Desconto de 10% para aposentados (maiores de 65)\n    if customer_age > 65 {\n        discount += 0.1;\n    }\n\n    // Desconto adicional de 5% para compras acima de 1000 reais\n    if price > 1000.0 {\n        discount += 0.05;\n    }\n\n    // Desconto máximo não superior a 30%\n    if discount > 0.30 {\n        discount = 0.30;\n                

Blogs

Book Recommendations