30 noviembre 2009

Test Driven Development: Pruebas de Software a priori


En Abril del 2007 di una platica en la UACJ que llevó el mismo título de este post.
Dicha plática se dio dentro de las actividades de la Sociedad de Estudios en Computacion, sociedad que liderea el profesor Saúl González.

Desempolvé este tema porque veo útil mostrar lo que es TDD (Desarrollo Orientado a Pruebas) y cómo TDD ayuda al desarrollo y al mantenimiento de un sistema. Tradicionalmente las pruebas de una aplicación  representan una tarea monótona y talachera  (entiéndase manual, repetitiva y sujeta al error humano).
Pones a un equipo de monos a ejecutar Test Cases y que Dios nos agarre confesados!!, la idea es que hagan pedazos la aplicación, la idea es que encuentren cual Sherlock Holmes todos los bugs habidos y por haber. Lo cual en la práctica, no es una tarea sencilla, depende de la pericia del tester, depende de que tan bien estén diseñadas las pruebas, depende de muchos factores.
En sistemas complejos, lo que más quieres es controlar esos factores, con TDD puedes controlar que las Pruebas Unitarias se cumplan, las cuales son un factor crucial en los sistemas diseñados bajo el Paradigma de Orientados a Objetos.

Que es TDD?
Es una metodología de desarrollo de Software que consiste en implementar las pruebas unitarias antes de comenzar a escribir el código de un modulo
Las pruebas unitarias consisten en comprobaciones (manuales o automatizadas) que se realizan para verificar que el código correspondiente  a un modulo concreto de un sistema funciona de acuerdo con los requisitos del sistema.
Ø  Tradicionalmente las pruebas se realizan a posteriori, es decir, después de codificar la funcionalidad
Ø  En TDD, las pruebas se preparan antes de comenzar a escribir el código
Primero se escriben los casos de prueba y después se implementa el código necesario para que la prueba pase con éxito

Por lo general las pruebas son métodos que ejecutan una acción específica, por ejemplo:
         CrearUsuario()
         CalcularArea()
         BloquearPassword()
         BorrarFactura()
         SacarLaBasuraPorLasMañanasAntesDeQuePaseElCamionRecolector()


Tipos de pruebas
Hay muchas pruebas que se le pueden (deben) hacer al software antes de liberarlo. De otra forma la calidad no va a estar garantizada. Hoy en día nadie se pondría una vacuna que no paso por años de pruebas, sin embargo en la industria de software aun hay Cromañones que no les cae el veinte con esto de las pruebas.
Existen varios tipos d pruebas, por citar algunos:
         Pruebas de usuario
         Pruebas cruzadas
         Pruebas Unitarias
         Pruebas de regresión

Que son las pruebas Unitarias en TDD?
En TDD una prueba unitaria no es otra cosa que código que permite probar los módulos mediante condiciones de prueba.
Las pruebas son diseñadas por el desarrollador y por los expertos del negocio. Estas pruebas se pueden automatizar usando una herramienta como NUnit. Una ventaja de automatizarlas es la posibilidad de lanzar las pruebas tantas veces como sean necesario. En caso de haber cambios en la funcionalidad al correr la prueba se puede verificar si se siguen cumpliendo las reglas de negocio de los objetos.

Ventajas de usar TDD
         Al escribir primero los casos de prueba, definimos de manera formal los requisitos que esperamos que cumpla nuestra aplicación. 
         Al escribir una prueba unitaria, pensamos en la forma correcta de utilizar un módulo que aún no existe.
         Se puede automatizar la ejecución de los casos de prueba (por ejemplo, con ayuda de algún framework de pruebas como NUnit)
         Los casos de prueba nos permiten perder el miedo a realizar modificaciones en el código.
         Los casos de prueba definen claramente cuándo termina nuestro trabajo (cuando pasan con éxito todos los casos de prueba)
         Se puede obtener un avance real de la fase de contruccion, basándonos en el code coverage 
         Esta práctica es compatible con el Desarrollo Agil (Agile Development).
         TDD permite fácilmente la refactorización.
         TDD fomenta la disciplina del equipo de programación

Proceso de desarrollo con TDD
         Escribir solo el código necesario, que cumpla con la funcionalidad requerida.
         Pasos de este proceso:
        Diseñar las pruebas unitarias
        Escribir el esqueleto de los modulos (o clases) necesarios para la prueba
        Escribir las pruebas unitarias
        Ejecutar las pruebas unitarias
        Codificar los mínimo requerido para que pase cada prueba unitaria asociada a un modulo.
        Ejecutar la prueba y verificar que pase correctamente.
        Iterar

Herramientas para hacer pruebas automáticas
         Existen un número considerable de herramientas que nos pueden ayudar para automatizar las pruebas unitarias.
         Usualmente son frameworks que permiten rápidamente escribir “código que prueba código”.
         La elección de la herramienta dependerá de la naturaleza del proyecto.
         Algunos de los más conocidos son:
        Microsoft Team System
        NUnit
        MbUnit
        JUnit
        csUnit
        TBrun
        XTest
        y muchos mas

Limitaciones de TDD
Hay que tener precaución al decidir cuándo usar TDD. No es recomendable usar TDD cuando:
  • Por la naturaleza de la aplicación  no sea factible automatizar las pruebas
  • No se tenga un conocimiento solido de la Programación Orientada a Objetos (no es suficiente usar lenguajes orientados a objetos, hay que pensar en objetos)
  • El equipo esté formado por desarrolladores poco experimentados. Se requieren fuertes habilidades técnicas y de análisis para producir buenas pruebas.
  • Los requerimientos no estén definidos en forma clara.
  • No se cuente con expertos en el negocio dentro del equipo
  • Exista mucha rotación de personal en el equipo


Si aplicas mal o a medias TDD puedes encontrarte los siguiente problemas:
         Generar pruebas mal diseñadas que no te sirvan para determinar si tus objetos tienen el comportamiento esperado.  La calidad de la prueba depende del conocimiento del desarrollador.
         Se puede invertir mucho tiempo en hacer pruebas y dejar corta la fase de construcción.
         Diseñar pruebas poco representativas, que no cumplan con un code coverage que en algún momento dejen de usarse
         Invertir mucho tiempo en hacer las pruebas y no darles mantenimiento, lo cual representa un gasto inútil de tiempo.

5 comentarios:

montecarlo dijo...
Este comentario ha sido eliminado por el autor.
montecarlo dijo...

Me ha parecido un artículo realmente interesante y me ha ayudado mucho para empezar a entender mejor este método.

En mi empresa, una cadena de supermercados internacional, estamos tratando de implantar el TDD (Test Drive Development) como metodología de pruebas.

Hace menos de un año introdujimos el scrum para la gestión de los proyectos y ha funcionado bastante bien. Así que ahora estamos dispuestos a lanzarnos en la implantación del TDD (Test Drive Development) como metolología para la realización de las pruebas.

Yo soy el responsable del diseño de dicha metodología, pero no se muy bien por donde empezar ya que nunca había oido hablar de ella y lo único que sé es a través de artículos de internet que he podido leer, tan buenos como el tuyo.

He podido ver que eres un experto en el tema de la metodología TDD, por lo que te estaría enormemente agradecido si me pudierais ayudar con algún tipo de información o consejo que me permitiera empezar a diseñar dicha metodología en mi empresa de forma adecuada, ya que actualmente me encuentro algo desorientado al respecto.

Muchas gracias por todo.

Recibe un cordial saludo y mi más sincera enhorabuena por el artículo

Eliezer Haubert dijo...

Muchas gracias por tus comentarios, me da gusto que sea de utilidad el post.
Antes de que te lances a usar TDD en tus proyectos de software, te recomiendo que consideres lo siguiente:
* Contar con un equipo de desarrolladores con un nivel de experiencia considerable, no te recomiendo que uses TDD si tienes un equipo de becarios.

* Contar con expertos de negocio para diseñar las pruebas o con una definición del requerimiento bastante clara y detallada.

* Fijar un porcentaje de code coverage, donde trabajo manejamos un 90%. Te recomiendo que uses alguna herramienta para ver el code coverage que llevas cubierto con tus pruebas. Por ejemplo, para proyectos de .Net puedes usar el la herramienta de testers que trae por defecto el Visual Studio Team System (En breve voy a poner un post sobre este tema). Es mas complicado llevar este control manualmente, por ejemplo con NUnit.

* Por otro lado, dependiendo de la arquitectura de tu proyecto, debes definir que partes vas a cubrir con las pruebas unitarias automáticas(no todo se puede probar en forma automática), necesitas establecer si tus pruebas se van a centrar en la base de datos, la capa de negocio, la capa de presentación, etc.

* Y por ultimo ve contemplando en tus estimados el tiempo que vas a requerir para hacer y mantener las pruebas, esta es la parte complicada, muchas veces los proyectos dejan de usar TDD por que requiere algo de mantenimiento.

Espero que te ayude la información, cualquier duda estoy a tus ordenes

Unknown dijo...

Hola Eliezer,

¿Qué tal las navidades? Espero que hayas disfrutado mucho.

Tus recomendaciones están muy bien la verdad, me han ayudado bastante para tener una mejor idea a la hora de empezar a implantar esta metodología.

En estos momentos estoy tratando de adaptar el TDD a mi empresa. En este punto me están surgiendo una serie de dudas más concretas. Tú que dominas estos temas, a ver si me puedes ayudar.

Te explico cuál sería nuestro ciclo de desarrollo de un proyecto software:

El primer paso sería determinar las pruebas de aceptación a partir de los requisitos definidos. ¿En este punto se empezarían ya a codificar estas pruebas de aceptación o esperamos a tener el siguiente paso realizado que se explica a continuación? Esta es mi primera duda.

El segundo paso que hacemos es desglosar cada requisito en tareas. Actualmente sobre cada una de estas tareas se escribe el código necesario y realizamos pruebas. Si ahora queremos aplicar la metodología TDD, primero definiremos las pruebas unitarias, luego con cada prueba se codificará y se realizará el código para que sea correcta la prueba.

Mi principal duda es en qué orden se realizarían los dos pasos anteriores y cuál es el proceso que deberíamos seguir para pasar de tener las pruebas de aceptación definidas a comprobar que son correctas.

A nivel de documentación, ¿sería interesante tener registro de todo tipo de pruebas?, ¿cómo y cuáles crees tu que sería mejor documentarlas o no merece la pena guardar registro de ninguna de las pruebas?.

Por otro lado podrías recomendarme alguna herramienta de software libre para análisis de cobertura de código en la ejecución de pruebas (Java, AS400, VB). ¿Cuál crees tu que sería el porcentaje de cobertura ideal? Sé que tú usas un 90% de cobertura, ¿crees que es lo ideal o estandar?

Espero que me aclares un poco todas estas ideas que te planteo.

Muchas gracias por todo, me eres de gran ayuda.

Un abrazo

Eliezer Haubert dijo...

Gracias Aris por los comentarios. Estoy revisando tus preguntas y te tengo unas sugerencias:
Sobre tu pregunta ¿En este punto se empezarían ya a codificar estas pruebas de aceptación?, te comento que si estás haciendo un desarrollo orientado a objetos va a ser sencillo y natural saber en que punto empezar a codificar las pruebas. Mi respuesta es que debes empezar a hacer las pruebas cuando tengas el diagrama de clases, y dirás, como voy a tener el diagrama si aun no haga las clases?, en este caso lo único que necesitas es la definición de la clase, el armazón, solo los métodos, propiedades y relaciones que van a tener tus clases.

En este caso puedes seguir un flujo de desarrollo, similar a lo siguiente:
-- Definir requisitos
-- A partir de los requisitos obtener casos de aceptación y casos de prueba
-- A partir de los requisitos generas el diseño de las clases (No te conviene diseñar las clases a partir de una sola prueba o una tarea, el alcance de tus clases sería muy limitado)
-- Ayudado de tus casos de aceptación y tu diagrama de clases comienzas a aplicar TDD.

El anterior seria un método digamos tradicional de aplicar TDD, el cual lleva implícito algunos riesgos:
-- Si los requisitos no están suficientemente claros o específicos las pruebas que diseñes sobre ellos van a ser insuficientes o innecesarias.
-- Por otro lado si tienes requerimientos completos, pero no tienes buenos programadores/analistas, las pruebas van a ser lo que se llama comúnmente como falsos positivos, es decir, pruebas que siempre van a pasar aunque en la realidad esten fallando. Me ha tocado ver casos en los que se prueba que 1=1, esto es una prueba válida y que siempre va a estar en verde, pero que no te va a servir para saber si la aplicación funciona bien o no.
-- Hacer pruebas unitarias efectivas, no es tarea sencilla y requiere algo de experiencia para saber que probar.

Sobre tu pregunta ¿sería interesante tener registro de todo tipo de pruebas?, puedes usar como documentación tus mismas pruebas, ahí tienes detalle de todo lo que pruebas. Conviene documentarlo todo?
Depende de que tanta documentación te pidan en tu proyecto, recuerda que los sistemas son entes vivos, cambian continuamente y la documentación debe ser de igual forma, te conviene usar documentación dinámica (comentarios XML en tu código), conforme cambien o se agreguen pruebas a tu proyecto, las documentación va a cambiar también. Hay herramientas como Ndoc o Sandcastle que formatean los comentarios XML.

Sobre la cobertura, mucha gente tiene la idea que con TDD se trata de hacerle pruebas a todo, la idea de TDD es que pruebes lo que realmente te de valor, por ejemplo te conviene mas enfocarte en la interface publica que en las definiciones de clases y métodos privados.

Se pensaría que cubrir el 100% del código con pruebas unitarias es algo bueno, en realidad es algo utópico. Tener una cobertura alta, es solo una pieza en la construcción de software de calidad, no lo es todo. En la industria es aceptable contar con un code coverage del 85 al 95%, pero insisto, esto es solo una medición, hay mas factores que indican si tu software es bueno o no.

Para tus pruebas unitarias en Java puedes usar JUnit, para el code coverage jcoverage, Cobertura, Emma, Clover, etc.

Saludos!!