Testes baseados em propriedades em Rust: como capturar bugs que você não procurava

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

Testes baseados em propriedades em Rust: a arte de capturar bugs que você não procurava

\n\n

Você já escreveu testes unitários, executou-os, tudo verde, e depois o aplicativo falhou na primeira entrada não padrão do usuário? Familiar. Testes clássicos são como verificar se uma fechadura funciona inserindo apenas sua própria chave. E se um invasor (ou apenas o acaso) trouxer uma chave de fenda, um clipe ou um pedaço de arame?

\n\n

Em Rust, uma linguagem conhecida por sua segurança, existe uma ferramenta poderosa que permite não apenas verificar cenários específicos, mas provar que o código se comporta corretamente para todos os dados de entrada possíveis. Isso é o teste baseado em propriedades. E hoje vamos analisá-lo na prática, com exemplos reais, para que você possa aplicar esse conhecimento imediatamente em seus projetos.

\n\n

O que é teste baseado em propriedades e como ele difere do teste comum?

\n\n

Imagine que você está testando uma função de ordenação. Um teste unitário comum se parece com isso:

\n\n
fn test_sort() {\n    let mut arr = vec![3, 1, 2];\n    arr.sort();\n    assert_eq!(arr, vec![1, 2, 3]);\n}
\n\n

Funciona? Funciona. Mas e se o array já estiver ordenado? E se estiver vazio? E se tiver 10.000 elementos em ordem inversa? Escrever testes para cada caso manualmente é um caminho sem fim.

\n\n

Teste baseado em propriedades inverte a abordagem. Em vez de definir dados de entrada específicos, você descreve uma propriedade (property) que deve ser verdadeira para qualquer entrada válida. E uma biblioteca especial (por exemplo, proptest ou quickcheck) gera centenas e milhares de casos de teste aleatórios, tentando encontrar aquele único que viola sua propriedade.

\n\n

Para ordenação, a propriedade pode ser: "O resultado da ordenação sempre tem o mesmo comprimento que o array original, e cada elemento do array original aparece no resultado exatamente uma vez". Ou, mais simplesmente: "O resultado está ordenado em ordem crescente".

\n\n

Por que Rust é o ambiente ideal para testes baseados em propriedades

\n\n

Rust já captura muitos erros em tempo de compilação (gerenciamento de memória, condições de corrida). Mas há uma classe de bugs que o compilador não vê: erros lógicos, tratamento incorreto de casos limite, comportamento inesperado com combinações específicas de dados. É aqui que entram os testes baseados em propriedades em Rust.

\n\n

Eles complementam perfeitamente o sistema de tipos: os tipos garantem que os dados tenham a estrutura correta, e os testes baseados em propriedades garantem que a lógica de processamento desses dados esteja correta para todo o espaço de valores possíveis.

\n\n

Ferramentas: proptest vs quickcheck

\n\n

No ecossistema Rust, existem dois frameworks principais: proptest e quickcheck. Ambos fazem a mesma coisa, mas com filosofias diferentes.

\n\n
    \n
  • quickcheck — mais antigo, minimalista. Funciona através da trait Arbitrary. Você escreve uma função que recebe dados aleatórios e o teste é executado várias vezes.
  • \n
  • proptest — moderno, poderoso, com várias estratégias de geração de dados. Permite ajustar finamente quais dados gerar e consegue "reduzir" (shrink) um exemplo com falha ao menor caso reproduzível. É o padrão ouro atualmente.
  • \n
\n\n

Usaremos o proptest, pois oferece mais controle e é melhor documentado. Vamos instalá-lo:

\n\n
# Cargo.toml\n[dev-dependencies]\nproptest = "1.4"
\n\n

Escrevendo nosso primeiro teste baseado em propriedades em Rust

\n\n

Vamos voltar à nossa ordenação. Vamos escrever um teste que verifica se o resultado está realmente ordenado:

\n\n
use proptest::prelude::*;\n\nfn is_sorted<T: Ord>(slice: &[T]) -> bool {\n    slice.windows(2).all(|w| w[0] <= w[1])\n}\n\nproptest!                

Blogs

Book Recommendations