Que es Test-Driven Development (TDD)? Y por qué deberías aprenderlo lo más pronto posible. Parte 1

Que es Test-Driven Development (TDD)? Y por qué deberías aprenderlo lo más pronto posible. Parte 1

Test-Driven Development (TDD)?  

En español el desarrollo guiado por pruebas es una metodología o proceso, el cual se basa en convertir los requerimientos en casos de prueba (test cases) antes de que el software sea completamente desarrollado, siguiendo todo el desarrollo con un testeo continuo de todos los casos. Este proceso de TDD es lo opuesto a desarrollar un software en primer lugar y luego crear pruebas, lo que en general es bastante riesgoso.



El TDD es un ciclo iterativo, el cual se puede modelar como se ve en la siguiente imagen:


Ejemplificación del Test Driven Development (TDD)

En primer lugar se debe definir una lista de requisitos y después se ejecuta el siguiente ciclo:


  1. Elegir un requisito: Se elige de una lista el requisito que se cree que nos dará mayor conocimiento del problema y que a la vez sea fácilmente implementable.

  2. Escribir una prueba: Se comienza escribiendo una prueba para el requisito. Para ello el programador debe entender claramente las especificaciones y los requisitos de la funcionalidad que está por implementar. Este paso fuerza al programador a tomar la perspectiva de un cliente considerando el código a través de sus interfaces. 

  3. Verificar que la prueba falla: Si la prueba no falla es porque el requisito ya estaba implementado o porque la prueba es errónea.

  4. Escribir la implementación: Escribir el cäódigo mas sencillo que haga que la prueba funcione. Se usa la expresión "Dejelo simple" ("Keep It simple, Stupid!") conocida como principio KISS.

  5. Ejecutar las pruebas automatizadas: Verificar si todo el conjunto de pruebas funciona correctamente.

  6. Eliminacion de duplicación: El paso final es la refractorización, que se utilizará principalmente para eliminar código duplicado. Se hace un pequenio cambio cada vez y luego se corren las pruebas hasta que funcionen.

  7. Actualización de la lista de requerimientos: Se actualiza la lista de requisitos tachando el requisito implementado. Por condiguiente se agregan requisitos que se hayan visto como necesarios durante este ciclo y se agregan requisitos de diseño (P. ej que una funcionalidad esté desacoplada de otra).

Tener un único repositorio universal de pruebas facilita complementar TDD con otra práctica recomendada por los procesos ágiles de desarrollo, la "Integración Continua". Integrar continuamente nuestro trabajo con el del resto del equipo de desarrollo permite ejecutar toda la batería de pruebas y así descubrir, si nuestra última versión es compatible con el resto del sistema.


Es recomendable y menos costoso corregir pequeños problemas cada pocas horas, que enfrentarse a problemas enormes cerca de la fecha de entrega fijada.


Estrategias de implementación

Uno de los puntos más delicados a la hora de aplicar TDD como herramienta de diseño es en el paso en el que ya tenemos un test que falla y debemos crear la implementación mínima para que el test pase.


Para ello Kent Beck expone un conjunto de estrategias que nos van a permitir avanzar en pasos pequeños hacia la solución del problema.


Implementación falsa

Una vez que tenemos el test fallando, la forma más rápida de obtener la primera implementación es creando un fake que devuelva una constante. Esto nos ayudará a ir progresando poco a poco en la resolución del problema, ya que al tener la prueba pasando estamos listos para afrontar el siguiente caso.


Triangular

 la técnica de la triangulación, es el paso natural que sigue a la técnica de la implementación falsa. Es más, en la mayoría de los contextos, forma parte de la triangulación, basándose en lo siguiente:


  1. Escoger el caso más simple que debe resolver el algoritmo.

  2. Aplicar el algoritmo del TDD.

  3. Repetir los pasos anteriores cubriendo las diferentes características.

Implementación obvia:

 Cuando la solución parece muy sencilla, lo ideal es escribir la implementación obvia en las primeras iteraciones del ciclo del TDD.


La estructura de un UnitTest: Arrange, Act and Assert (AAA) Pattern

Estructura de un Unit Test o Test de Unidad

El patron AAA (Arrange, Act and Assert) se corresponde a organizar, actuar y afirmar (OAA ?). Se refiere a que deberias dividir tu metodo o funcion de test en estas tres secciones.


Cada una de ellas es responsable entonces por la parte correspondiente a como su nombre indica:


Arrange (Organizar)

En esta primera parte solo debes tener codigo necesario para configurar el test especifico. Es en esta face donde se crean los objetos necesarios, se implementan los simulacros (mocks) y se establecen posibles espectativas.


Act (Actuar)

En esta parte se invocan los metodos que estan siendo testeados o puestos a prueba.


Assert (afirmar)

Esta ultima face es en general la mas corta y en ella se comprueba si se han cumplido las expectativas expuestas en el test. Es decir, se define si el test es exitoso o ha fallado.


El patron AAA

El patron AAA lo vemos presente en el siguiente código:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
TEST(MyDBTest, LoginTest) {
	
	// Arrange
	using_GMock::MockDB mdb;
	using_GMock::MyDatabase db(mdb);

	EXPECT_CALL(mdb, login("Bienvenidos", "Thewhitecode.com"))
		.Times(1)
		.WillOnce(Return(true));

	// Act
	int retVal = db.Init("Bienvenidos", "Thewhitecode.com");

	// Assert
	EXPECT_EQ(retVal, 1);
}

En este ejemplo, para la fase de organización, inicializamos los objetos que testearemos: using_GMock::MockDB mdb y using_GMock::MyDatabase db.


Adicional a esto usamos el macro EXPECT_CALL , el cual nos permite crear una "Expectativa" a una funcion de simulacro (función mock).


EXPECT_CALL tiene dos argumentos. El primero es el objeto de simulacro (mock object) y el segundo es la funcion o metodo a testear junto con sus argumentos.


Con ayuda de las funciones Times( ) y WillOnce( ) especificamos que la funcion a testear (en este caso login( ) ) debe llamarse una vez y retornar el valor true respectivamente.


En la parte de actuar simplemente llamamos a la función con los parametros deseados y guardamos el valor de retorno en la variable retVal.


Por último en la parte de afirmar comprobamos si el valor que nos retorna la función corresponde a 1. Si es así, entonces nuestro test ha sido exitoso.


Sanchez

Profesional en informatica medica con enfasis en algoritmos y analizis de imagen en C++. Programador con experiencia en C/C++ y Python.

Reactions

8

1

0

0

Access hereTo be able to comment

TheWhiteCode.com is not the creator or owner of the images shown, references are: