avatar

Andres Jaimes

Desarrollo Guiado por Pruebas (TDD)

By Andres Jaimes

- 4 minutes read - 814 words

El desarrollo guiado por pruebas o Test-driven development (TDD) es una metodología que consiste en los siguienten pasos:

  1. Escribir la lista de pruebas que cubra la nueva funcionalidad;
  2. Elegir un elemento de la lista y escribir una prueba unitaria mínima y concreta;
  3. Escribir el código que permita que la prueba unitaria pase, sin ir más allá.
  4. Opcionalmente, hacer pequeñas mejoras al código (refactoring);
  5. Volver al punto 2 y repetir el proceso hasta terminar con los elementos de la lista.

A continuación algunas notas del libro de Kent Beck, creador de esta metodología.

Lista de pruebas

¿Qué deberías probar? Antes de empezar, escribe una lista con todas las pruebas que sepas que vas a tener que programar. La primera parte del enfoque para lidiar con el estrés de la programación es no dar nunca un paso adelante a menos que tengamos una idea de cual será nuestro siguiente paso. Cuando nos sentamos a una sesión de programación, ¿qué es exactamente lo que pretendemos conseguir?

Podemos iniciar con la siguiente pregunta: ¿qué comportamiento vamos a necesitar para que el sistema muestre el funcionamiento requerido? Dicho de otra manera: ¿qué conjunto de pruebas, una vez pasadas, demostrará que existe un código en el que confiamos que genere la salida solicitada?

Cogí el hábito de apuntar en un trozo de papel, al lado del ordenador, todo lo que quería conseguir en las próximas horas. Tenía una lista similar pegada en la pared, pero con un alcance semanal o mensual. En cuanto lo tenía todo apuntado, sabía que no me iba a olvidar de nada. Cuando surgía algo nuevo, decidía de forma rápida y consciente si iba a la lista de “ahora”, a la de “luego” o si realmente no hacía falta hacerlo.

En lugar de esbozar las pruebas en una lista, podríamos simplemente lanzarnos a implementarlas todas. Hay un par de razones por las que escribir pruebas en masa no me ha funcionado. Primero, cada prueba que implementas es un poco de inercia extra cuando te toca refactorizar. Con las herramientas de refactorización automática (por ejemplo, cuando tienes una opción de menú que renombra la declaración y todos los usos de una variable), esto es menos problemático. Pero cuando has implementado diez pruebas y luego descubres que los argumentos tienen que ir en el orden inverso, es mucho menos probable que te pongas a limpiar el código. Segundo, si tienes diez pruebas fallando, estás muy lejos de la barra verde. Si quieres volver a verde rápido, tienes que desechar las diez pruebas. Si quieres que todas funcionen, vas a tener que quedarte mirando una barra roja durante mucho tiempo.

A medida que haces que las pruebas pasen, la propia implementación te sugerirá nuevas pruebas. Apúntalas en la lista. Haz lo mismo con las refactorizaciones.

Hay que ocuparse de las cosas que queden en la lista al terminar la sesión. Si te has quedado realmente a medias con una funcionalidad, usa esa misma lista más tarde. Si has descubierto refactorizaciones más grandes que están fuera de alcance por el momento, muévelas a la lista de “luego”. No recuerdo haber movido nunca un caso de prueba a la lista de “luego”. Si se me ocurre una prueba que podría no funcionar, conseguir que funcione es más importante que lanzar mi código.

Prueba primero

¿Cuándo deberías escribir tus pruebas? Antes de escribir el código que vas a probar. No vas a probar después. Tu objetivo como programador es que la funcionalidad esté en marcha. Sin embargo, necesitas una forma de pensar en el diseño y necesitas un método para controlar el alcance de lo que haces.

Aserciones primero

¿Cuándo deberías escribir las aserciones (asserts)? Intenta escribirlas primero.

¿Por dónde deberías empezar a construir un sistema? Por las historias que quieres poder contar sobre el sistema ya terminado.

¿Por dónde deberías empezar a escribir una funcionalidad? Por las pruebas que quieres que pase el código definitivo.

¿Por dónde deberías empezar a escribir una prueba? Por las aserciones que pasarán cuando esté lista.

Jim Newkirk me mostró esta técnica. Cuando hago las pruebas empezando por la aserción (assert-first), noto que tiene un potente efecto simplificador. Al escribir una prueba estás resolviendo varios problemas a la vez, incluso aunque ya no tengas que pensar en la implementación.

Por ejemplo, supongamos que queremos comunicarnos con otro sistema a través de la red. Iniciemos a escribir nuestra aserción partiendo del final:

1testVerificarDatos() {
2  assertTrue(respuesta.isClosed());
3  assertEquals("abc", respuesta.contents());
4}

¿De dónde viene la respuesta? Del lector:

1testVerificarDatos() {
2  Buffer respuesta = lector.contents();
3  assertTrue(respuesta.isClosed());
4  assertEquals("abc", respuesta.contents());
5}

¿Y el lector? Éste lo obtenemos de una conexión a un servidor:

1testVerificarDatos() {
2  Socket lector = Socket("localhost", defaultPort());
3  Buffer respuesta = lector.contents();
4  assertTrue(respuesta.isClosed());
5  assertEquals("abc", respuesta.contents());
6}

Y así continuamos el proceso hasta tener una prueba completa.

Referencias