E se seus testes unitários estiverem mentindo?
\nVocê escreveu uma centena de testes unitários. Eles passam. O código em C++ parece perfeito. Mas quando você executa o aplicativo em produção, ele falha com segmentation fault na requisição 100.500. Dor familiar?
\nTestes unitários tradicionais verificam apenas o que você mesmo imaginou. Eles são cegos para cenários que não lhe vieram à mente. Testes baseados em propriedades (ou property-based testing, PBT) resolvem esse problema radicalmente: eles não verificam exemplos específicos, mas provam propriedades (properties) do seu código para milhões de dados de entrada aleatórios.
\nNeste artigo, vamos explorar como implementar testes baseados em propriedades em C++ com a biblioteca RapidCheck e ver como eles capturam bugs que os testes unitários comuns deixam passar. Pronto para fazer seu código sofrer de verdade? Vamos lá.
\n\nTestes baseados em propriedades: filosofia em 30 segundos
\nImagine que você está testando uma função de ordenação. Teste comum: sort([3,1,2]) == [1,2,3]. Ok, verificou um caso.
Abordagem baseada em propriedades: "Para qualquer lista de inteiros, após a ordenação, o comprimento não muda, os elementos estão em ordem crescente e cada elemento da lista original está presente na lista ordenada". O framework gera centenas de listas aleatórias (incluindo vazias, com duplicatas, com números negativos) e verifica essas propriedades.
\nIdeia-chave: você descreve invariantes (properties) que devem ser sempre verdadeiros, em vez de entradas e saídas específicas.
\n\nQuando o PBT é especialmente útil em C++?
\n- \n
- Algoritmos e estruturas de dados (ordenação, busca, grafos) \n
- Parsers e serializadores (codificação/decodificação) \n
- Funções matemáticas (operações inversas, intervalos de valores) \n
- Gerenciamento de memória (vazamentos, double free) \n
- Qualquer código com invariantes claros \n
RapidCheck: seu novo melhor amigo
\nEm C++, existem várias bibliotecas para PBT, mas RapidCheck é a mais madura e conveniente. Ela é inspirada na biblioteca QuickCheck de Haskell e se integra perfeitamente com Google Test e Catch2.
\nA instalação é simples: é uma biblioteca header-only, basta copiar os arquivos de include/rapidcheck para o seu projeto. Ou use Conan/vcpkg.
Primeiro teste: propriedade de operação inversa
\nSuponha que temos uma função que codifica uma string em base64 e a decodifica de volta. Qual propriedade é óbvia? "Decodificar após codificar retorna a string original".
\n#include <rapidcheck.h>\n#include <string>\n\nstd::string encode_base64(const std::string& input);\nstd::string decode_base64(const std::string& input);\n\nint main() {\n rc::check("Decodificar após codificar retorna o original",\n [](const std::string& original) {\n // Gerador de string já embutido\n auto encoded = encode_base64(original);\n auto decoded = decode_base64(encoded);\n RC_ASSERT(decoded == original);\n });\n return 0;\n}\n\nO que está acontecendo aqui? RapidCheck gera strings aleatórias (incluindo vazias, com Unicode, com bytes nulos). Para cada uma, ele verifica a propriedade. Se encontrar um contraexemplo — ele o minimiza (shrinking), ainda quebrando o teste. Isso é ouro: você não recebe apenas "há um bug", mas um caso específico e reproduzível.
\n\nPropriedades avançadas: mais que igualdade
\nMonotonicidade e idempotência
\nPara a função remove_duplicates:
- \n
- Idempotência: se aplicada duas vezes, o resultado não muda. \n
- Ordem não importa: a lista resultante contém os mesmos elementos que a original, mas sem duplicatas. \n
#include <vector>\n#include <algo