Nota del autor

Si la entrada que estás leyendo carece de imágenes, no se ve el vídeo que teóricamente lleva incrustado o el código fuente mostrado aparece sin formato, podéis conocer los motivos aquí. Poco a poco iré restableciendo la normalidad en el blog.
Este blog es un archivo de los artículos situados previamente en Lobosoft.es y ha dejado de ser actualizado. Las nuevas entradas pueden encontrarse en www.lobosoft.es. Un saludo,
Lobosoft.

jueves, 19 de febrero de 2009

Doubler, automatizando la generación de pruebas unitarias

Cuando llevamos a cabo un desarrollo guiado por pruebas (TDD) ideal, las pruebas serán escritas con antelación al código que deberá implementar la funcionalidad deseada, y a continuación se escribirá este último, de forma tal que supere aquellas. Es decir, como hemos comentado en varias ocasiones, las pruebas unitarias forman parte de las especificaciones de los requerimientos que deberemos cumplir. Sin embargo, este entorno ideal de iteraciones (escribir pruebas, codificar, pasar las pruebas y refactorizar) no siempre puede darse. En ocasiones, por limitaciones de tiempo se prima el que la funcionalidad esté lista sin llevar a cabo pruebas unitarias, o bien “heredamos” código ya implementado, y antes de meternos de pleno con él optamos por crear una batería de pruebas que nos ayude a programar con algo más de seguridad.


En casos como estos, es bastante útil contar con generadores de código que automaticen la creación de un “esqueleto” de pruebas unitarias. En el caso de usar el framework de pruebas MSTest de Microsoft, bastará con tener un Visual Studio para que, seleccionando los componentes a probar, genere un proyecto de pruebas unitarias que podremos “rellenar”. Incluso usando Pex podemos conseguir pruebas unitarias que validen casos extremos, ya que realiza un análisis de nuestro código. Por cierto, esta herramienta de Microsoft que aún está en alfa únicamente funciona con las ediciones Team System de Visual Studio.


Sin embargo, no siempre queremos o podemos usar el framework de pruebas que nos propone Microsoft. Bien por políticas de empresa, por licenciamiento, o porque preferimos cualquier otro, la generación de las pruebas para MSTest requiere realizar una serie de cambios sobre el código autogenerado, como renombrar atributos de clases y métodos y refactorizar un poco los fuentes. Si preferimos NUnit o MbUnit para implementar nuestras pruebas, podemos usar Doubler para, a partir de un ensamblado .NET, generar el esqueleto de pruebas unitarias de forma automática. Doubler está disponible en el sitio de Google Code, y se trata de un add-in para Lutz Roeder’s Reflector, que posiblemente conozcáis bien si soléis trabajar con .NET. Su instalación es muy sencilla (basta con añadir la DLL correspondiente desde el menú de gestión de add-ins), y permite acceder a un menú contextual desde el que le indicaremos qué deseamos hacer con una determinada clase. Desde el mismo podemos llevar a cabo diversas acciones:




  • Generación de pruebas: Con botón derecho sobre un tipo inmediato, la generación de pruebas hace aparecer una lista de propiedades configurables (lenguaje de generación, framework de pruebas, que incluye MSTest, NUnit y MbUnit, inclusión de atributos…) para la generación automática de código de pruebas. Tras pulsar sobre el botón de generación (que es tan grande que no lo parece), se creará un fichero de código de pruebas que deberemos completar con los Asserts correspondientes.

  • Grabación de un generador: Se aplica a tipo abstractos, creando un stub para las pruebas. Un Test Stub es un objeto usado en un tests para reemplazar al componente real. En nuestro caso, el Recording Test Stub puede ser usado para verificar salidas indirectas de las pruebas. En cierto modo es como crear un inspector de comportamiento para el componente que está siendo sometido a pruebas.

  • Generador de Fakes: Crea un falso objeto, basado en una clase abstracta, para ser usado en las pruebas sobre una clase base. El falso objeto, o fake, deberá dar una implementación alternativa a la funcionalidad que el objeto al que reemplaza.

  • Wrapper o generador de interfaces: Aplicado sobre un tipo inmediato, crea una copia de la interfaz del tipo seleccionado, así como una implementación de la misma que redirige todas las llamadas a una instancia privada del mismo a través. Esto permite un desacoplamiento de las pruebas respecto al objeto.


La verdad es que el código que crea es bastante claro y nos puede ahorrar, como decía tiempo y trabajo repetitivo con un par de clics de ratón. Si al mismo le sumamos que Pex también cuenta ya con un add-in para Reflector (y que funciona siempre que tengamos Pex instalado en nuestra máquina, es decir, si contamos con un Visual Studio TS), lo cierto es que la herramienta se va a convertir en un compañero aún más inseparable de lo que ya lo era.


A continuación muestro algunas capturas básicas con la generación de unos métodos de prueba para una clase muy simple, a modo de Hola Mundo con Doubler ;) .





Un par de bloques de nuestro código a probar...


La estructura de la DLL para Reflector.


El menú contextual añadido por el add-in Doubler.


Estableciendo las propiedades para el código generado


Y nuestro código de pruebas, listo para ser completado y usado.

5 comentarios:

  1. He de confesar que no confío mucho en las directrices del TDD; eso de desarrollar las pruebas antes de codificar..., con los documentos de requisitos "demasiado dinámicos" de los proyectos de mi empresa fijo que NO funciona ;-).

    Así todo, he de decir que me encanta este área del desarrollo de software y que me debería poner algún día con ello. Más aún tras comprobar que existen addins gratuitos como Doubler (¡gran descubrimiento de proyecto, sí señor!) que te permiten automatizar tareas de testing sin ceñirte única y exclusivamente a frameworks de testing propios de Microsoft.

    Una vez más, me quito el sombrero ante tus posts relacionados con testing de código. Ahora sólo falta que de leerte, se me pegue algo ;-).

    ¡SaludoX!

    ResponderEliminar
  2. Esto Myth si que suena a ciencia ficción:

    " las pruebas serán escritas con antelación al código que deberá implementar la funcionalidad deseada "

    El otro dia en el capítulo de BCP del CISSP decían también algo así como que el Disaster Recovery Plan debia comenzar a elaborarse en la fase inicial de todo proyecto, ¿alguien hace cualquiera de las dos cosas?

    Teoria Vs Practica Vs Negocio

    ResponderEliminar
  3. ¡Buenas compañeros!

    Jejeje, la verdad es que el tema de seguir a rajatabla el TDD es algo utópico, como señala GigA, pero sobre todo porque el negocio manda y generalmente en los desarrollos no se valoran adecuadamente aspectos tales como las pruebas o la seguridad :) Obviamente, en empresas dedicadas a la consultoría, que posiblemente no mantengan el código que desarrollan, o facturen por ello, estos aspectos serán menos relevantes, pero si desarrollamos aplicaciones internas, o que venderemos a clientes y que habremos de mantener durante mucho (muuuuucho) tiempo, creo que el esfuerzo merece la pena.

    Ahora bien, el codificar antes de tener las especificaciones, obviamente requiere de unos mínimos en lo tocante a las especificaciones. Codificar algo, pruebas unitarias incluidas, que posiblemente sea cambiado, implica que deberemos codificar nuevamente bastante código. Tal vez podríamos llegar a un término medio, si tenemos requerimientos muy cambiantes, si realizamos las pruebas de lo que sea así, sí o sí (pongamos un cálculo financiero, la gestión de una serie de entidades que posiblemente no cambien...), e ir cerrando las pruebas con herramientas automáticas como Doubler para los métodos que no hayamos podido probar en un principio. De todas formas, de la teoría (¿utopía?) a la realidad siempre va un trecho, pero se hace más llevadero si nos ayudamos en lo posible con este tipo de prácticas.

    Y, Lonifasiko, todos aprendemos de todos (ahí creo que está el "enganche" de los blogs). Acabo de poder leer vuestras entradas sobre la caída de Gmail (¡¡ay, que se acaba el mundo!! :P ), y la tuya sobre dev \efort me encantó.

    ¡Un saludo para ambos!

    ResponderEliminar
  4. Me "autocomento" para incluir una serie de enlaces que me han parecido interesantes sobre TDD, que he encontrado en otro blog:

    http://carlossantos.wordpress.com/2008/03/12/tdd-y-las-cosas-felizmente-no-volvieron-a-ser-las-mismas/

    http://carlossantos.wordpress.com/2008/03/16/tdd-por-dnde-empezar/

    http://carlossantos.wordpress.com/2008/04/13/tdd-ahora-refactoring/

    http://carlossantos.wordpress.com/2008/04/15/tdd-otros-atributos-de-nunit/

    Un saludo.

    ResponderEliminar
  5. Weberger, me alegro que te haya sido de utilidad (o al menos, despertado tu curiosidad) la herramienta que os propongo. Respecto al caso que me indicas, te recomiendo pasar por

    http://carlossantos.wordpress.com/2008/03/16/tdd-por-dnde-empezar/

    y

    http://geeks.ms/blogs/csegura/archive/2009/04/19/mi-particular-visi-243-n-del-test-driven-development-tdd.aspx

    pues tienen algunos artículos sobre el tema. Aparte de esto, sí que te recomendaría hacerte con un libro, bastante interesante, y que explica de forma clara cómo empezar con la revisión de pruebas unitarias y el TDD. Se trata del "Test-Driven Development in Microsoft .NET", de James W. Newkirk y Alexei A. Vorontsov, si trabajáis con tecnologías .NET, o el más genérico "Test Driven Development: By Example", de Kent Beck. Son imprescindibles :)

    Ya nos contarás qué tal tus avances en el tema. Un cordial saludo.

    ResponderEliminar