Testes baseados em propriedades em Java: quando um teste substitui mil
\n\nVocê já escreveu um teste unitário que passava verde, mas uma semana depois falhava em produção com um erro em "algum valor estranho"? Familiar? O teste comum (baseado em exemplos) é como verificar se a chaleira ferve apenas a 100°C. E se a pressão for diferente? E se a altitude for diferente? O teste baseado em propriedades em Java resolve exatamente esse problema: em vez de percorrer manualmente exemplos, você descreve propriedades que seu código deve satisfazer, e o framework gera centenas de dados de entrada aleatórios e verifica se está tudo certo.
\n\nEssa abordagem é especialmente útil para desenvolvedores Java que escrevem algoritmos complexos, trabalham com coleções, parsers ou funções matemáticas. Neste artigo, vamos explorar como implementar property-based testing no seu projeto usando a biblioteca jqwik e mostrar exemplos reais de código.
\n\nO que é teste baseado em propriedades e como ele difere do teste comum?
\n\nNa abordagem clássica, você escreve:
\n\n@Test\nvoid testAddition() {\n assertEquals(4, Calculator.add(2, 2));\n assertEquals(0, Calculator.add(-1, 1));\n}\n\nTudo bem, até que alguém passe Integer.MAX_VALUE. Ou null. Ou um número negativo muito grande. O teste baseado em propriedades inverte a lógica: você não diz "espero tal resultado", mas "para quaisquer dados de entrada válidos, o resultado satisfaz a propriedade X".
Por exemplo, uma propriedade para adição: add(a, b) == add(b, a) (comutatividade). Ou add(a, 0) == a (elemento neutro). O framework gera milhares de pares aleatórios a e b e verifica se a propriedade não é violada.
Principais vantagens do property-based testing:
\n- \n
- Geração automática de dados de teste — você encontra bugs que nem imaginava. \n
- Redução na quantidade de testes — um teste de propriedade substitui dezenas de testes baseados em exemplos. \n
- Verificação de casos limite — os geradores sabem criar valores "perigosos" (0, null, strings vazias, números grandes). \n
- Shrinking — se o teste falhar, o framework minimiza os dados de entrada até o exemplo mais simples que reproduz o erro. \n
Bibliotecas para teste baseado em propriedades em Java
\n\nExistem várias soluções maduras no mercado:
\n- \n
- jqwik — moderna, leve, integra-se perfeitamente com JUnit 5. Recomendo para começar. \n
- QuickTheories — rápida, mas menos popular. \n
- JUnit QuickCheck — fork de uma ferramenta Scala antiga, mas com suporte deficiente. \n
Usaremos jqwik, pois possui a API mais amigável e excelente documentação. Adicione a dependência no pom.xml:
<dependency>\n <groupId>net.jqwik</groupId>\n <artifactId>jqwik</artifactId>\n <version>1.8.4</version>\n <scope>test</scope>\n</dependency>\n\nOu para Gradle:
\n\ntestImplementation 'net.jqwik:jqwik:1.8.4'\n\nEscrevendo nosso primeiro teste baseado em propriedades em Java
\n\nSuponha que temos um método que inverte uma string:
\n\npublic static String reverse(String s) {\n if (s == null) return null;\n return new StringBuilder(s).reverse().toString();\n}\n\nQual propriedade podemos verificar? Se invertermos a string duas vezes, obtemos a string original. Essa é uma propriedade ideal para property-based testing.
\n\nimport net.jqwik.api.*;\nimport static org.assertj.core.api.Assertions.assertThat;\n\nclass StringProperties {\n