Pruebas y calidad de código

TDD: Ciclo Red-Green-Refactor en la práctica

«¿Escribir pruebas primero? Si aún no hay código, ¿qué pruebo?»

4 min de lectura
Imagen de portada de TDD: Ciclo Red-Green-Refactor en la práctica

«¿Escribir pruebas primero? Si aún no hay código, ¿qué pruebo?»

Al conocer TDD (Test-Driven Development, desarrollo guiado por pruebas), todos pensamos lo mismo. El orden parece invertido y da la sensación de duplicar el trabajo sin motivo.

Sin embargo, tras usarlo durante unos meses en el trabajo real, la perspectiva cambia.

TDD es una forma de desarrollo que repite en ciclos muy breves «prueba fallida (Red) → código mínimo que la hace pasar (Green) → limpieza (Refactor)». La clave es ejecutar el ciclo rápidamente, de unos segundos a unos minutos.

Hoy explicaré paso a paso cómo funciona realmente este ciclo Red-Green-Refactor.


TDD Red-Green-Refactor: resumen de las 3 etapas clave ✍️

Primero, veamos el ciclo completo de un vistazo.

  1. Red: escribir primero una prueba para una funcionalidad que aún no existe. Naturalmente, falla.
  2. Green: escribir solo el código mínimo necesario para superar la prueba. No tiene que ser bonito.
  3. Refactor: limpiar el código manteniendo las pruebas superadas.

Eso es todo: agrupar estas tres etapas y repetirlas continuamente.

TDD consiste en recorrer estas tres etapas una y otra vez
TDD consiste en recorrer estas tres etapas una y otra vez

Lo importante es no hacer demasiado grande cada etapa. Hay que dividirla en partes pequeñas: no «toda la función de inicio de sesión», sino algo como «devolver un error si el correo está vacío».

Si un ciclo tarda 30 minutos, probablemente no sea TDD, sino desarrollo con pruebas añadidas.


Etapa Red: ¿por qué escribir código que falla a propósito?

Es la parte más difícil de entender al aprender TDD. Sabes que fallará, así que ¿por qué ejecutarlo?

La razón es sencilla. Para comprobar que la prueba «falla correctamente».

Si una prueba pasa justo después de escribirla, puede ser una carcasa vacía que no valida nada. Ver primero el fallo equivale a probar la propia prueba.

Como ejemplo sencillo, supongamos que creamos una función que suma dos números. La prueba aparece antes que el código.

// add La función aún no existe. Por eso esta prueba falla(Red)
struct AddTests {
    @Test func Suma_1sumar2es3() {
        #expect(add(1, 2) == 3)
    }
}

Al ejecutarlo así, aparece un error como «cannot find ‘add’ in scope». Ver esa luz roja incluso me tranquiliza, porque el objetivo queda claro.

A partir de ahora solo tengo una tarea: convertir esa luz roja en verde.

Ver esta luz roja deja clara la siguiente tarea
Ver esta luz roja deja clara la siguiente tarea

Green y Refactor: aquí se demuestra la verdadera habilidad

El principio de la etapa Green es «mantenerlo tan simple que dé vergüenza».

Este es el código más rápido que supera la prueba anterior.

// El código mínimo que supera la prueba(Green)
func add(_ a: Int, _ b: Int) -> Int {
    return a + b
}

Parece obvio, ¿verdad? Sin embargo, los principiantes suelen preocuparse por el futuro e intentan añadir de antemano manejo de excepciones, registros y valores de configuración.

TDD obliga deliberadamente a contener ese impulso. La regla es no escribir código que la prueba actual no requiera.

Solo después de encenderse la luz verde se pasa a Refactor. Entonces se mejoran los nombres de variables y se elimina la duplicación.

La clave es no añadir nunca funcionalidad nueva durante Refactor. Solo se mejora la estructura mientras las pruebas siguen pasando.

¿Y si aparece la luz roja mientras limpias el código? Basta con deshacer lo que acabas de tocar. Las pruebas garantizan que funcionaba hace un momento.

Gracias a esta red de seguridad, la refactorización se volvió mucho más atrevida.

Esta sensación de convertir la luz roja en verde hace que quieras seguir
Esta sensación de convertir la luz roja en verde hace que quieras seguir

¿TDD ralentiza el desarrollo? 🤔

Es la pregunta que más recibo. Tras usarlo, mi respuesta fue: «depende».

He resumido en una tabla las diferencias que noté.

Criterio Con TDD Pruebas escritas después
Velocidad inicial Algo más lenta Rápida
Momento en que se detectan los errores Inmediatamente después de escribir Mucho después
Carga de refactorización Baja Alta
Lógica compleja Favorable Desfavorable

Sinceramente, TDD puede resultar aparatoso al crear rápidamente una sola pantalla sencilla.

En cambio, brilló claramente con lógica llena de condiciones, como pagos, liquidaciones y cálculos de descuentos. Al fijar cada caso con pruebas, ya no me daba miedo modificar el código más adelante.

Al final, TDD no es una solución universal, sino más bien una herramienta para controlar la complejidad.


Para terminar

Si TDD te resulta difícil, no empieces a lo grande. Prueba Red-Green-Refactor primero con una sola función.

Cuando los pequeños ciclos de éxito se vuelven naturales, empiezas a entender por qué la gente no puede abandonar este método. Espero que hoy enciendas la luz roja con una sola prueba.

Lecturas recomendadas