Seu teste unitário mente (e você sabe disso)
\n\nImagine: você escreveu 50 testes unitários, todos verdes, cobertura de 90%. Você está satisfeito. Mas, uma semana depois, a produção cai. Acontece que seu método SortArray() funciona perfeitamente para [1, 2, 3] e [3, 1, 2], mas falha em [int.MaxValue, -1, 0]. Familiar?
O problema dos testes unitários clássicos é o viés do desenvolvedor. Você escreve testes para os dados que espera. Hackers, usuários reais e condições de contorno aleatórias seguem outras regras.
\n\nÉ aqui que entram os testes baseados em propriedades em C# (ou property-based testing). Em vez de verificar "se A, então B", você descreve propriedades que devem ser sempre verdadeiras. E o framework gera centenas de entradas aleatórias e verifica suas afirmações.
\n\nNeste artigo, vamos explorar como implementar testes baseados em propriedades em C# usando a biblioteca FsCheck, escrever exemplos reais e ver como eles capturam bugs que os testes comuns ignoram.
\n\nO que são testes baseados em propriedades?
\n\nTestes baseados em propriedades são uma abordagem onde você formula invariantes (propriedades) do seu programa. Por exemplo:
\n- \n
- "Após a ordenação, o comprimento do array não muda" \n
- "A ordem inversa da reversão de uma string resulta na string original" \n
- "A soma de dois números positivos é sempre positiva" \n
O framework (por exemplo, FsCheck) gera dezenas e centenas de entradas aleatórias e verifica se a propriedade é válida para todas. Se um contraexemplo for encontrado — o teste falha, e você obtém os valores específicos que quebraram o código.
\n\nAo contrário dos testes unitários, você não escreve Assert.AreEqual(3, Add(1,2)). Você escreve Assert(x + y == y + x) — e testa a comutatividade para milhares de pares de números.
Diferença dos testes unitários comuns
\n\n| Testes unitários | Testes baseados em propriedades |
|---|---|
| Verificam exemplos específicos | Verificam leis gerais |
| Dados definidos pelo desenvolvedor | Dados gerados pelo framework |
| Ignoram casos de borda | Exploram automaticamente os limites |
| Fáceis de ler | Exigem pensamento abstrato |
Biblioteca FsCheck: sua principal ferramenta
\n\nNo mundo C#, o padrão de facto para testes baseados em propriedades é o FsCheck (originalmente um port da biblioteca Haskell QuickCheck). Ele se integra com xUnit, NUnit e MSTest.
\n\nA instalação é simples:
\ndotnet add package FsCheck\ndotnet add package FsCheck.Xunit // para integração com xUnit\n\nFsCheck é capaz de gerar dados aleatórios para tipos primitivos (int, string, float), listas, arrays, tuplas e até tipos personalizados (através de geradores customizados).
Escrevendo nosso primeiro teste baseado em propriedades em C#
\n\nVamos começar com algo simples: verificar se a operação de adição de números inteiros é comutativa.
\n\nusing FsCheck;\nusing FsCheck.Xunit;\n\npublic class AdditionTests\n{\n [Property]\n public bool Commutative_Addition(int x, int y)\n {\n return x + y == y + x;\n }\n}\n\nExecute o teste. O FsCheck gerará 100 pares aleatórios de int (por padrão) e verificará a propriedade. Se nenhum contraexemplo for encontrado — o teste fica verde. Se tivéssemos errado (por exemplo, escrito x - y == y - x), o teste falharia com um exemplo específico: (5, 3) — porque 5 - 3 != 3 - 5.
Analisando o código
\n- \n
- [Property] — atributo que informa ao FsCheck que este é um teste baseado em propriedades. \n
- O método retorna
bool— se retornarfalse, o teste falha. \n - Parâmetros
int x, int y— o FsCheck substituirá automaticamente por valores aleatórios.