Pruebas basadas en propiedades en TypeScript: cómo detectar errores que no buscabas

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Introducción: por qué las pruebas unitarias no salvan de todo

\n\n

Imagina que escribiste una función de ordenamiento de arrays. La cubriste con una docena de pruebas unitarias: array vacío, un elemento, lista ya ordenada, orden inverso. Las pruebas pasan en verde. Confías en el código. Pero una semana después, la producción falla — con un array de 1000 elementos con duplicados y números negativos. ¿Te suena familiar?

\n\n

Las pruebas en TypeScript tradicionales basadas en ejemplos (example-based testing) solo verifican los escenarios que imaginaste. ¿Y si el error se esconde en una combinación en la que no pensaste? Aquí entra en escena el testing basado en propiedades (property-based testing) — un enfoque que genera automáticamente miles de datos de entrada aleatorios y verifica los invariantes de tu código.

\n\n

En este artículo, analizaremos cómo aplicar property-based testing en TypeScript con la librería fast-check, qué propiedades vale la pena verificar y cómo esto cambia el enfoque del desarrollo.

\n\n

¿Qué son las pruebas basadas en propiedades?

\n\n

En lugar de escribir:

\n
it('ordena [3, 1, 2] → [1, 2, 3]', () => {\n  expect(sort([3, 1, 2])).toEqual([1, 2, 3]);\n});\n
\n\n

Describes una propiedad (property) — una afirmación que debe ser verdadera para todos los datos de entrada válidos:

\n
it('el array ordenado no cambia el orden de los elementos', () => {\n  fc.assert(\n    fc.property(fc.array(fc.integer()), (arr) => {\n      const sorted = sort(arr);\n      // Propiedad 1: la longitud se conserva\n      expect(sorted.length).toBe(arr.length);\n      // Propiedad 2: cada elemento del array original está presente\n      // Propiedad 3: el array está ordenado de forma ascendente\n      for (let i = 1; i < sorted.length; i++) {\n        expect(sorted[i - 1]).toBeLessThanOrEqual(sorted[i]);\n      }\n    })\n  );\n});\n
\n\n

La librería fast-check generará por sí misma cientos de arrays aleatorios (incluyendo vacíos, con NaN, con números decimales, con valores enormes) y ejecutará tu prueba en cada uno. Si alguna falla — te enterarás del problema.

\n\n

¿Por qué es necesario específicamente en TypeScript?

\n\n

TypeScript proporciona tipado estático, pero no garantiza la corrección de la lógica. El testing basado en propiedades en TypeScript cierra la brecha entre «los tipos coinciden» y «el código funciona correctamente». Es especialmente útil para:

\n\n
    \n
  • Funciones de transformación de datos (map, filter, reduce)
  • \n
  • Analizadores sintácticos y validadores
  • \n
  • Algoritmos con lógica no trivial
  • \n
  • Funciones que trabajan con valores límite
  • \n
\n\n

Librería fast-check: inicio rápido

\n\n

Instala fast-check en el proyecto:

\n
npm install --save-dev fast-check\n
\n\n

Sintaxis básica:

\n
import fc from 'fast-check';\n\ndescribe('propiedades de la función reverse', () => {\n  it('reverse(reverse(arr)) === arr', () => {\n    fc.assert(\n      fc.property(fc.array(fc.anything()), (arr) => {\n        expect(reverse(reverse(arr))).toEqual(arr);\n      })\n    );\n  });\n});\n
\n\n

Aquí fc.array(fc.anything()) es un generador de arrays arbitrarios. Puedes combinar generadores:

\n
    \n
  • fc.integer() — números enteros
  • \n
  • fc.string() — cadenas de texto
  • \n
  • fc.record({ name: fc.string(), age: fc.integer({ min: 0, max: 120 }) }) — objetos
  • \n
  • fc.oneof(...) — selección aleatoria entre varios generadores
  • \n
\n\n

¿Qué propiedades verificar? 5 ejemplos prácticos

\n\n

1. Invariancia (reversibilidad)

\n

Cualquier operación que tenga una inversa debe ser reversible:

\n
it('serialización y deserialización de JSON', () => {\n  fc.assert(\n    fc.property(fc.object(), (obj) => {\n      const json = JSON.stringify(obj);\n      const parsed = JSON.parse(json);\n      expect(parsed).toEqual(obj);\n    })\n  );\n});\n<                

Blogs

Book Recommendations