Desarrollo impulsado por prueba

El desarrollo guiado por pruebas (TDD) es un caso especial de programación basada en pruebas que añade el elemento del diseño continuo.

La programación basada en pruebas implica la creación de pruebas unitarias automatizadas para el código de producción. antes Escribes ese código de producción. En lugar de escribir las pruebas después (o, más comúnmente, no escribirlas nunca), siempre empiezas con una prueba unitaria. Para cada pequeña funcionalidad en el código de producción, primero creas y ejecutas una prueba pequeña (idealmente muy pequeña) y específica que define y valida qué hará el código. Esta prueba podría no compilar al principio, porque puede que no existan todas las clases y métodos que requiere. Sin embargo, funciona como una especificación ejecutable. Luego la haces compilar con el mínimo código de producción necesario, para poder ejecutarla y observar si falla. (A veces esperas que falle, y pasa, lo cual es información útil). Finalmente, generas exactamente el código necesario para que la prueba pase.

Esta técnica resulta extraña, al principio, para muchos programadores que la prueban. Es como si los escaladores subieran poco a poco por una pared de roca, colocando anclajes a medida que avanzaban. ¿Por qué tanto lío? ¿Acaso no ralentiza considerablemente el proceso? La respuesta es que solo tiene sentido si, posteriormente, se depende mucho de esas pruebas unitarias. Quienes practican el desarrollo guiado por pruebas (TDD) con regularidad afirman que el esfuerzo invertido en escribirlas compensa con creces el resultado.

Para el desarrollo guiado por pruebas (TDD), normalmente se utiliza uno de los frameworks de pruebas unitarias automatizadas de la familia xUnit (JUnit para Java, NUnit para C#, etc.). Estos frameworks facilitan la creación, ejecución, organización y gestión de grandes conjuntos de pruebas unitarias. (Al menos en el mundo Java, están cada vez mejor integrados en los mejores IDE). Esto es beneficioso, ya que al trabajar con TTD, se acumula una gran cantidad de pruebas unitarias.

Beneficios del trabajo basado en pruebas.

Los conjuntos exhaustivos de pruebas unitarias automatizadas funcionan como una red para detectar errores. Determinan, de forma precisa y determinista, el comportamiento actual del sistema. Los equipos que siguen una metodología de desarrollo guiado por pruebas (TDD) observan que obtienen muchos menos defectos a lo largo del ciclo de vida del sistema y dedican mucho menos tiempo a la depuración. Las pruebas unitarias bien escritas también constituyen una excelente documentación de diseño que, por definición, siempre está sincronizada con el código de producción. Un beneficio algo inesperado: muchos programadores afirman que la barra verde que indica que todas las pruebas se ejecutan correctamente se vuelve adictiva. Una vez que te acostumbras a estas pequeñas y frecuentes señales de retroalimentación positiva sobre el estado de tu código, es muy difícil renunciar a ellas. Finalmente, si el comportamiento de tu código está bien definido gracias a numerosas pruebas unitarias de calidad, es mucho más fácil de manejar. safer para ti refactorizar el códigoSi una refactorización (o un ajuste de rendimiento, o cualquier otro cambio) introduce un error, tus pruebas te alertarán rápidamente.

Desarrollo guiado por pruebas: llevándolo más allá

El desarrollo guiado por pruebas (TDD) es un caso especial de programación basada en pruebas que incorpora el diseño continuo. Con TDD, el diseño del sistema no está restringido por un documento de diseño en papel. En cambio, se permite que el proceso de escribir pruebas y código de producción guíe el diseño a medida que se avanza. Cada pocos minutos, se refactoriza para simplificar y clarificar. Si se puede imaginar fácilmente un método, clase o modelo de objetos completo más claro y conciso, se refactoriza en esa dirección, protegido en todo momento por un sólido conjunto de pruebas unitarias. La premisa de TDD es que no se puede determinar con certeza qué diseño será el más adecuado hasta que se esté inmerso en el código. A medida que se aprende qué funciona y qué no, se está en la mejor posición para aplicar esos conocimientos, mientras aún están frescos en la memoria. Y toda esta actividad está protegida por los conjuntos de pruebas unitarias automatizadas.

Podrías empezar con bastante diseño previo, aunque lo más habitual es comenzar con bastante diseño de código simpleEn el mundo de la programación extrema, a menudo bastan algunos bocetos UML en pizarra. Sin embargo, con TDD, importa menos la cantidad de diseño inicial que la flexibilidad que se le permita tener a medida que se avanza. Quizás no se realicen cambios arquitectónicos radicales, pero sí se puede refactorizar el modelo de objetos en gran medida si se considera lo más conveniente. Algunas empresas tienen mayor libertad política que otras para implementar TDD puro.

Pruebas primero frente a depuración

Es útil comparar el esfuerzo dedicado a escribir pruebas iniciales con el tiempo dedicado a depurar. Depurar suele implicar revisar grandes cantidades de código. El enfoque de desarrollo guiado por pruebas (TDD) permite concentrarse en fragmentos manejables, donde hay menos posibilidades de que algo falle. Para los responsables de desarrollo es difícil predecir cuánto tiempo llevará realmente la depuración. Y, en cierto modo, gran parte del esfuerzo de depuración se desperdicia. Depurar implica inversión de tiempo, estructura y recursos (puntos de interrupción, monitorización de variables temporales, instrucciones de impresión) que, en esencia, son desechables. Una vez que se encuentra y se corrige el error, todo ese análisis se pierde. Y si no se pierde por completo para el desarrollador, sin duda se pierde para otros programadores que mantengan o amplíen ese código. Con el enfoque TTD, las pruebas están disponibles para que todos las usen, indefinidamente. Si un error reaparece, la misma prueba que lo detectó una vez puede volver a detectarlo. Si surge un error porque no hay una prueba coincidente, se puede escribir una prueba que lo capture a partir de entonces. De esta forma, muchos defensores del desarrollo guiado por pruebas afirman que es la máxima expresión de trabajar de forma más inteligente en lugar de trabajar más duro.

Técnica y herramientas de prueba primero

No siempre es sencillo escribir una prueba unitaria para cada aspecto del comportamiento de un sistema. ¿Qué sucede con las interfaces gráficas de usuario (GUI)? ¿Y con los EJB y otros componentes gestionados por frameworks basados ​​en contenedores? ¿Qué ocurre con las bases de datos y la persistencia en general? ¿Cómo se prueba que una excepción se lanza correctamente? ¿Cómo se evalúa el rendimiento? ¿Cómo se mide la cobertura, la granularidad y la calidad de las pruebas? La comunidad de desarrollo guiado por pruebas (TDD) está respondiendo a estas preguntas con un conjunto de herramientas y técnicas en constante evolución. Se sigue invirtiendo una enorme cantidad de ingenio para hacer posible cubrir cada aspecto del comportamiento de un sistema con pruebas unitarias. Por ejemplo, a menudo resulta útil probar un componente de un sistema de forma aislada de sus colaboradores y recursos externos, utilizando objetos simulados (fakes) y objetos de prueba. Sin estos objetos simulados, las pruebas unitarias podrían no ser capaces de instanciar el objeto bajo prueba. O, en el caso de recursos externos como conexiones de red, bases de datos o interfaces gráficas de usuario (GUI), el uso del recurso real en una prueba podría ralentizarla enormemente, mientras que el uso de una versión simulada o de prueba mantiene todo funcionando rápidamente en memoria. Y si bien algunos aspectos de la funcionalidad siempre pueden requerir pruebas manuales, el porcentaje para el cual eso es indiscutiblemente cierto continúa disminuyendo.