¿Y si las pruebas se escriben solas? Conociendo las pruebas basadas en propiedades
\n\n¿Alguna vez has escrito una prueba unitaria, la has ejecutado, has obtenido una marca verde, y un mes después has encontrado un error en la misma función? ¿Te suena familiar? Las pruebas unitarias clásicas son como verificar que tu cartera no está vacía mirando solo un billete. Verificas un escenario concreto, un conjunto específico de datos de entrada. ¿Y si te dijera que hay una forma de hacer que la computadora invente por sí misma miles de escenarios diferentes y verifique si tu código se rompe?
\n\nEsto no es ciencia ficción. Son las pruebas basadas en propiedades en JavaScript (o property-based testing). En lugar de escribir "si paso 2 y 3, obtengo 5", describes una propiedad de tu función: "para cualquier par de números, el resultado de la suma será un número que siempre es mayor o igual que cada uno de los sumandos". Y el entorno de pruebas genera cientos de pares de números aleatorios, verificando esta afirmación.
\n\nEn este artículo, analizaremos cómo funcionan las pruebas basadas en propiedades, en qué se diferencian de las pruebas normales y cómo implementarlas en tu proyecto con la excelente librería fast-check. Verás lo fácil que es encontrar errores "fronterizos" que ni siquiera habrías pensado en buscar.
¿Por qué las pruebas unitarias normales no siempre salvan?
\n\nVamos al grano. Supongamos que tienes una función que ordena un array de números de forma ascendente:
\n\nfunction sortNumbers(arr) {\n return arr.slice().sort((a, b) => a - b);\n}\n\nEscribes una prueba unitaria:
\n\ntest('ordena el array [3, 1, 2]', () => {\n expect(sortNumbers([3, 1, 2])).toEqual([1, 2, 3]);\n});\n\nLa prueba es verde. Eres feliz. Pero, ¿qué pasa si el array contiene null? ¿O undefined? ¿O números muy grandes? ¿O un array de 10000 elementos? No escribirás una prueba para cada uno de estos casos. Pero las pruebas basadas en propiedades en JavaScript sí lo harán.
En lugar de enumerar ejemplos, describes invariantes (propiedades) que siempre deben ser verdaderas:
\n- \n
- La longitud del array ordenado es igual a la longitud del array original. \n
- Cada elemento del array original está presente en el array ordenado. \n
- Para cualquier índice
i, se cumple quearr[i] <= arr[i+1]. \n
Estas son precisamente las propiedades que verificará el testing de propiedades.
\n\nLa librería fast-check: tu aliada en las pruebas aleatorias
\n\nEn el mundo de JavaScript, la herramienta principal para las pruebas basadas en propiedades es fast-check. Puede generar datos aleatorios de cualquier tipo: números, cadenas, arrays, objetos, fechas e incluso estructuras complejas.
\n\nLa instalación es sencilla:
\nnpm install fast-check --save-dev\n\nAhora escribamos nuestra primera prueba basada en propiedades para la función sortNumbers. Usaremos el framework Jest, pero fast-check funciona con cualquier ejecutor de pruebas.
const fc = require('fast-check');\n\ntest('la ordenación conserva la longitud del array', () => {\n fc.assert(\n fc.property(fc.array(fc.integer()), (arr) => {\n const sorted = sortNumbers(arr);\n return sorted.length === arr.length;\n })\n );\n});\n\n¿Qué está pasando aquí? fc.array(fc.integer()) es un generador que crea arrays aleatorios de números enteros aleatorios. fc.property toma este generador y verifica la afirmación (nuestra función flecha) en 100 (por defecto) conjuntos de datos diferentes. Si al menos una prueba falla, fc.assert lanzará un error y mostrará un contraejemplo.
Añadiendo más propiedades
\n\nUna propiedad está bien, pero tres es mejor. Verifiquemos que después de ordenar, el array realmente está ordenado:
\n\ntest('la ordenación devuelve un array ordenado', () =>