Pruebas basadas en propiedades en Java: cuando una prueba reemplaza a mil
\n\n¿Alguna vez ha escrito una prueba unitaria que pasaba en verde, pero una semana después fallaba en producción con un error por un "valor extraño"? Le suena. Las pruebas habituales (basadas en ejemplos) son como comprobar que una tetera hierve solo a 100°C. ¿Y si la presión es diferente? ¿Y si la altitud sobre el nivel del mar es otra? Las pruebas basadas en propiedades en Java resuelven precisamente este problema: en lugar de repasar ejemplos manualmente, usted describe las propiedades que debe cumplir su código, y el framework genera cientos de datos de entrada aleatorios y verifica que todo esté en orden.
\n\nEste enfoque es especialmente útil para desarrolladores Java que escriben algoritmos complejos, trabajan con colecciones, analizadores sintácticos o funciones matemáticas. En este artículo, analizaremos cómo implementar property-based testing en su proyecto con la librería jqwik y mostraremos ejemplos reales de código.
\n\n¿Qué son las pruebas basadas en propiedades y en qué se diferencian de las habituales?
\n\nEn el enfoque clásico, usted escribe:
\n\n@Test\nvoid testAddition() {\n assertEquals(4, Calculator.add(2, 2));\n assertEquals(0, Calculator.add(-1, 1));\n}\n\nTodo está bien, hasta que alguien pasa Integer.MAX_VALUE. O null. O un número negativo muy grande. Las pruebas basadas en propiedades invierten la lógica: usted no dice "espero tal resultado", sino "para cualquier entrada válida, el resultado cumple la propiedad X".
Por ejemplo, una propiedad para la suma: add(a, b) == add(b, a) (conmutatividad). O add(a, 0) == a (elemento neutro). El framework genera miles de pares aleatorios a y b y verifica que la propiedad no se rompa.
Principales ventajas de property-based testing:
\n- \n
- Generación automática de datos de prueba — encuentra bugs en los que ni siquiera había pensado. \n
- Reducción de la cantidad de pruebas — una prueba de propiedades reemplaza decenas de pruebas basadas en ejemplos. \n
- Verificación de casos límite — los generadores pueden crear valores "peligrosos" (0, null, cadenas vacías, números grandes). \n
- Shrinking — si la prueba falla, el framework minimiza los datos de entrada hasta el ejemplo más simple en el que se reproduce el error. \n
Librerías para pruebas basadas en propiedades en Java
\n\nEn el mercado hay varias soluciones maduras:
\n- \n
- jqwik — moderna, ligera, se integra perfectamente con JUnit 5. La recomiendo para empezar. \n
- QuickTheories — rápida, pero menos popular. \n
- JUnit QuickCheck — fork de una antigua herramienta Scala, pero el soporte es deficiente. \n
Usaremos jqwik, ya que tiene la API más cómoda y una documentación excelente. Agregue la dependencia en 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\nO para Gradle:
\n\ntestImplementation 'net.jqwik:jqwik:1.8.4'\n\nEscribimos nuestra primera prueba basada en propiedades en Java
\n\nSupongamos que tenemos un método que invierte una cadena:
\n\npublic static String reverse(String s) {\n if (s == null) return null;\n return new StringBuilder(s).reverse().toString();\n}\n\n¿Qué propiedad podemos verificar? Si invertimos la cadena dos veces, obtenemos la cadena original. Esta es una propiedad ideal para property-based testing.
\n\nimport net.jqwik.api.*;\nimport static org.assertj.core.api.Assertions.assertThat;\n\nclass StringProperties {\n