Table-driven testing con it.each en React

Resumen

Cuando escribes pruebas que repiten el mismo flujo cambiando solo entradas y salidas, caes en un antipatrón. El table-driven testing resuelve ese problema centralizando los casos de prueba en un arreglo, lo que mejora la mantenibilidad de tus tests en React y reduce duplicación de código.

Por qué deberías usar table-driven testing en tus pruebas

Esta técnica te permite ejecutar múltiples escenarios con un solo bloque de test, iterando sobre una lista de entradas y resultados esperados.

¿Qué es table-driven testing? Es una metodología de pruebas en la que defines un arreglo con varios casos (inputs y outputs esperados) y los ejecutas con la misma lógica de aserción. Funciona ideal cuando varios casos comparten el mismo flujo pero esperan resultados distintos.

Los cuatro beneficios que justifican adoptarla son claros:

  • Mantenibilidad: editar un array es más simple que tocar cada caso de uso individualmente.
  • Legibilidad: tienes todas las entradas y salidas centralizadas en un solo sitio.
  • Eficiencia: un solo test ejecuta todos los casos que quieras cubrir.
  • Cobertura: es más difícil olvidar casos pequeños cuando los ves juntos.

Cuándo conviene aplicar esta técnica

Funciona cuando un componente recibe distintas combinaciones de props pero el flujo interno es el mismo. Un ejemplo perfecto es un componente Calculator que recibe dos números (a, b) y una operation (suma, resta, multiplicación, división), y por dentro resuelve con un switch case [0:55].

Si bien las entradas cambian, el procedimiento es idéntico: tomar los dos números, leer la operación y devolver el resultado. Ese patrón es la señal para aplicar table-driven testing.

Cómo escribir un test table-driven con Vitest y Testing Library

El flujo arranca creando un archivo calculator.test.tsx e importando describe, it y expect desde Vitest, junto con render y screen de @testing-library/react [2:05].

Dentro del suite, defines un arreglo useCasesTest con cada caso configurado como objeto. Cada objeto incluye las props del componente y un campo extra expected que representa el resultado esperado:

tsx const useCasesTest = [ { a: 1, b: 2, operation: 'sum', expected: 3 }, { a: 3, b: 2, operation: 'multiply', expected: 6 }, ];

El truco está en usar it.each para iterar el arreglo. Piénsalo como un foreach en el mundo de los tests:

tsx it.each(useCasesTest)( 'debería retornar $expected cuando a y b son $operation', ({ a, b, operation, expected }) => { render(<Calculator a={a} b={b} operation={operation} />); const result = screen.getByText(Result: ${expected}); expect(result).toBeInTheDocument(); } );

Cómo interpolar los valores en el nombre del test

Usando el signo $ seguido del nombre de la key, Vitest reemplaza ese marcador con el valor real del caso al ejecutar. Así, en consola ves mensajes como "debería retornar 3 cuando a y b son sum", lo que facilita identificar qué caso falló.

¿Por qué pasar las props como objeto al renderizar? Porque al destructurar { a, b, operation, expected } en el callback, necesitas enviarlas en formato objeto al componente. Si las pasas como argumentos sueltos, el componente recibe undefined y cae en el caso default del switch, devolviendo invalid operation.

Cómo validar que tus tests realmente funcionan

Una forma rápida de comprobar que las aserciones están corriendo es romperlas a propósito. Si esperas que 3 * 2 = 6 y cambias el expected a 9, el test debe fallar con un mensaje claro: el resultado es 6 pero tú esperabas 9.

Ese pequeño ejercicio confirma que el ciclo de iteración y la aserción toBeInTheDocument están leyendo correctamente el render del componente.

Casos especiales que vale la pena cubrir

Cuando agregues operaciones como la división, considera el escenario donde b es 0. Ese caso debe arrojar un error o un mensaje específico, y es justo el tipo de detalle que table-driven testing te ayuda a no olvidar, porque lo agregas como una fila más del arreglo.

  • Suma de dos positivos.
  • Resta con resultado negativo.
  • Multiplicación por cero.
  • División entre cero (caso especial).

Con esa estructura, agregar un nuevo caso es cuestión de sumar un objeto al arreglo, sin reescribir lógica de test. ¿Ya identificaste un componente en tu proyecto que pida a gritos esta técnica? Cuéntame en los comentarios cuál sería tu primer candidato para refactorizar.