semáforoLa práctica de Desarrollo Guiado por Pruebas (TDD) nos hace encarar la programación con un nuevo enfoque, pensando y escribiendo primero las pruebas y luego la implementación. Pero además de escribir primero la prueba, podemos avanzar aún más: hacer que las pruebas pasen exitosamente lo antes posible, incluso aunque esto signifique escribir código "de mentira" para hacer pasar a la prueba en cuestión.

Esta es una idea liberadora: al hacer TDD no es necesario implementar la funcionalidad real después de escribir una prueba, sino simplemente escribir código que la haga pasar, por más que no sea la implementación real. Esto nos dará tiempo para definir la interfaz mentalmente, y cómo deberá comportarse nuestro método.

Kent Beck, en su libro Test Driven Development by Example, explica algunos patrones para pasar de rojo a verde lo más rápidamente posible. Estos patrones se conocen como Patrones de Barra Verde.

Los Patrones de Barra Verde

Una vez que tenemos una prueba que falla, necesitamos arregarla. Si tratamos a cada prueba roja como una condición para arreglarla lo más rápido posible, vamos a lograr llegar al verde rápidamente. Los siguientes patrones ayudan a hacer pasar las pruebas (incluso aunque el resultado no sea algo que nos gustaría ver que sobreviva por más de una hora).

Implementación falsa

Después de tener una prueba que falla, ¿cuál es la primera implementación? Devolver una constante. A medida que vamos agregando pruebas, vamos transformando gradualmente esa constante en expresiones y variables.

Por ejemplo, supongamos una prueba para un cálculo de factorial. El método de prueba sería: 

 
@Test
public void calcularFactorial() {
    assertEquals(120, matematica.calcularFactorial(5));
}
 

La primer implementación de mentira del objeto matematica sería: 

 
public int calcularFactorial(int valor) {
    return 120;
}
 

Hacerlo de mentira nos ayuda a progresar de a poco: sabemos que la estructura de código no es la definitiva, y también sabemos que podemos ir mejorando con seguridad, ya que tenemos las pruebas en verde.

¿Por qué escribir algo que después vamos a tirar? Porque es mejor tener algo a no tener nada, especialmente si tenemos una prueba para comprobarlo. Hay varios efectos que hacen de este patrón una práctica poderosa: 

  • Psicológico: tener una barra verde es completamente distinto a tener una barra roja. Cuando la prueba pasa (verde) sabemos donde estamos parados. Tenemos total seguridad de hacer refactors y mejoras.
  • Control de alcance: los programadores son muy hábiles para imaginar problemas futuros. Esta práctica nos permite enfocarnos en el problema real, y no confundirnos en situaciones imaginarias.

Triangular

Triangular es el siguiente paso después de hacer una Implementación falsa, para intentar llegar a la solución genérica.

Debemos empezar a triangular cuando ya tenemos dos o más ejemplos/pruebas escritas sobre el mismo método. Siguiendo con el ejemplo del factorial, podemos agregar otra verificación para calcular el factorial de 0.

 
@Test
public void calcularFactorial() {
    assertEquals(120, matematica.calcularFactorial(5));
    assertEquals(1, matematica.calcularFactorial(0));
}

Lo que nos llevaría a la siguiente implementación de mentira: 

 
public int calcularFactorial(int valor) {
    if (valor == 0) {
        return 1;
    }
    return 120;
}

Con esto tenemos un ejemplo para cada rama de decisión. Podríamos ahora agregar una prueba para forzar a escribir el else:

 
@Test
public void calcularFactorial() {
    assertEquals(1, matematica.calcularFactorial(0));
 
    assertEquals(24, matematica.calcularFactorial(4));
    assertEquals(120, matematica.calcularFactorial(5));
}

Y si seguimos con la implementacion falsa tendríamos: 

 
public int calcularFactorial(int valor) {
    if (valor == 0) {
        return 1;
    } 
    else {
        if (valor == 4) return 24;
        else return 120;
    }
}

A esta altura, ya es evidente que sería más facil escribir la implementación obvia que seguir haciendo ramas de decisión.

 
public int calcularFactorial(int valor) {
    if (valor == 0) {
        return 1;
    }
    return valor * calcularFactorial(valor - 1);
}

Implementación obvia

¿Y cómo implementar las operaciones simples? Simplemente, implementándolas.

Los patrones de Implementación Falsa y Triangulación son para ir dando pasos pequeños, ir descubriendo el terreno. A veces ya sabemos bien cómo implementar una operación. Adelante, a escribirla. Por ejemplo, ¿realmente usaríamos el patrón de Implementación Falsa para una operación suma()? En general no. Simplemente escribimos la implementación obvia, y listo. Si de pronto aparecen barras rojas (pruebas fallidas), recurrimos a la implementación falsa.

Entonces, si sabemos bien cómo implementar la operación, simplemente lo hacemos. Pero hay que recordar que nos estamos demandando perfección. Y no somos perfectos. Tenemos que llevar un registro de la cantidad de barras rojas que obtenemos al usar la Implementación Obvia. Si constantemente nos fallan las pruebas, estamos malgastando el tiempo. En general es más facil ir avanzando de a poco con implementaciones falsas, que tratar de arreglar una Implementación Obvia que no funciona (no era tan obvia, después de todo).

Basado en Test-driven development by example, capítulo 28 (por Kent Beck) y TDD: making the test green quickly.

Inspiración.

"Si tú tienes una manzana y yo tengo una manzana e intercambiamos las manzanas, entonces tanto tú como yo seguiremos teniendo una manzana cada uno. Pero si tú tienes una idea y yo tengo una idea, e intercambiamos las ideas, entonces ambos tendremos dos ideas"

Bernard Shaw