Mostrando las entradas con la etiqueta TDD. Mostrar todas las entradas
Mostrando las entradas con la etiqueta TDD. Mostrar todas las entradas

jueves, enero 31, 2008

Code Coverage para Identificar Casos de Pruebas Importantes

Esta herramienta es muy útil. Nos ayuda a ver que porcentaje de nuestro código esta realmente siendo ejecutado. Hay código que nunca se toca y esto nos puede indicar tres cosas.

1. Es código que realmente no se usa y sólo estorba y perjudica a la mantenibilidad de nuestro proyecto. Solución: Borralo.

2. Es código que no se está probando. Solución: Crea una nueva prueba.

3. El código es tan simple que no hace falta probarlo. Esto pudiera ser real, sin embargo, si en todo el set de pruebas no se pasa nunca por ahí nos hace pensar que tal vez nunca se usa en toda la aplicación.

El punto uno es simple, solo no le agarren cariño al código que no sirve y presionen delete sin llorar por las horas que invirtieron en algo que nadie usara. El punto dos se puede cumplir de muchas formas, la ideal es una prueba unitaria que podamos repetir constantemente, sin embargo, podríamos hacer un WebTest o Manual Test, lo importante es que se pruebe.

Podemos ver un ejmplo reciente.

image

En este caso vemos que nuestro metodo Borrar Artículo siempre se esta probando cuando el baseNode no tiene un artículo. Esto pudiera no ser muy critíco porque los otros tres metodos si estan cubiertos por otras pruebas, sin embargo, es una prueba básica que debería estar considerada.

No es necesario llevar esta practica a un extremo y querer tener 100% Code Coverage, en muchos proyectos no vale la pena, sobre todo con código heredado o si no se inicio con buenas prácitcas. Lo que si debemos tener bien presente es que no baje nuestro code coverage, es decir, iteración tras iteración o mejor aún (Si usan Integración Continua) check-in tras check-in  nuestro porcentaje de cobertura aumente, esta es una buena metrica para el equipo completo. 

Links

Como Configurar Code Coverage en Team System (MSDN)
Configurar Integración Continua en Team System 2005
Configurar Integración Continua en Team System 2008
Code Coverage para NCover (Si pruebas con NUnit)

martes, enero 22, 2008

Aspect Oriented Programming Aplicado - PostSharp, Logging y HttpSimulator

Siempre me había intrigado por AOP y es que la idea de programar basado en aspectos en lugar de funciones o metodos o código secuencial es bastante atractiva. Obviamente no es para todo código, pero hay muchos "aspectos" que quisieramos poder tener encapsuladas y no sólo en un objeto, sino, despreocuparnos y no tener que invocarlas nosotros mismos.

Ejemplo Simple - Logging.

Pensemos que queremos loggear excepciones.

image

Ok, ese el código feo de novato. Ahora refactoricemos un poco. Podría estar en una clase y para cuestiones de la prueba hagamosla estática.

image

Ahora apliquen eso a todos los metodos de la clase. ¿Qué? Bueno supongan que nos interese enterarnos en nuestro archivo de Log de todos los lugares donde lleguue a ocurrir una excepción. Vamos a convertir esto en un aspecto. Refactoricemos de nuevo.

image

Simplemente hicimos nuestra clase serializable (necesario por PostSharp), hereda de OnExceptionAspect, overrideamos el Metodo OnException y listo, Logueamos tal como antes.

image

Ahora simplemente decoramos nuesto metodo con nuestro nuevo atributo, corremos la app y listo. El atributo también podría ir en una clase y aplicarle algunos filtros. Estas son sólo algunas de las posibilidades.

Puedes descargar el código aquí

Por mantener breve el ejemplo, la clase loggin es muy simple, en una aplicación real sugeriría usar el Loggin Application Block que es parte del Enterprise Library o Log4Net, ambos son open source y podrían integrarse con PostSharp.

Para un ejemplo aún más simple que este, vean el Video de Intro de PostSharp. El ejemplo me parecio poco práctico, sin embargo, es muy ilustrativo respecto a como usar la herramienta y sólo dura 5 minutos. Pueden bajar el código del video aquí.

Otro Ejemplo Simple - HttpSimulator

Hace algún tiempo escribi respecto al HttpSimulator, desde entonces lo hemos tenido que usar muchas veces para simular el contexto de Http en clases que dependen de algun objeto de ASP.NET y queremos probarla sin necesidad de que tenga dependencias a los objetos reales del framework, es decir, no queremos tener que usar realmente sesión y pasar por todo el pipeline de un request web, sólo para probar un simple metodo.

En el código a descargar viene todo el HttpContext, ahorita nos enfocaremos a las pruebas unitarias, estilo TDD para el HttpContext y como usarlo con PostSharp, en la prueba usaremos un WebDependentClass

image

Eso esperamos que arroje una excepcion, ya que WebDependentClass se debería ejecutar en un contexto web para que pueda usar HttpContext.Current tal como se muestra.

image

Ahora veamos como se haría esto con el HttpSimulator para lo que creamos otra prueba.

image

Es simple, se obtiene el path, se crea la instancia, se empieza a simular el request, se hace lo que se tenga que hacer y al salir del using se ejecuta el dispose y termina la simulación. Nuestra prueba pasa y todos contentos. Sólo que es mucha talacha. Puede haber 126 pruebas más que usen el Simulator. Aunque se podría simplificar un poco la creación, terminaríamos aún así con código repetido y el using no tenemos manera de evitarlo.

Con AOP, tenemos algo como lo siguiente:

image

El atributo se encarga de todo, inclusive si todas las pruebas de nuestro TestClass o nuestro Assembly de pruebas necesitaran del simulador podríamos definir el atributo a nivel de clase o assembly para aplicarles el mismo aspecto.

La implementación del atributo es de lo más simple. Tal como hicimos con el Logging, tenemos la clase serializable y hereda de un atributo predefinido en PostSharp.

image

Parece un Surround with snippet no? Es similar sólo que para tiempo de compilación.

Si quieres usarlo en tus pruebas, simplemente agrega el Assembly HttpSimulator y el atributo a las que dependan de un contexto Http. Puedes bajar el código de aquí.

Links

Downloads

SuperSimpleSample
LoggingSample
Attribute para el HttpContextSimulator
Todos los samples de este post
PostSharp Installer

PostSharp

PostSharp en .NET Rocks
PostSharp
Video de Intro de PostSharp

HttpSimulator

Post viejo mio
HttpSimulator

Miguel Madero

lunes, julio 16, 2007

Simular el HttpContext desde ASP.NET

Les dejo el link de Phil que hizo una librería super sencilla de usar
http://haacked.com/archive/2007/06/19/unit-tests-web-code-without-a-web-server-using-httpsimulator.aspx
Todo lo que haces es iniciar la simulación y listo, todas las llamadas a tu código pueden usar algo como HttpContext.Current.Application como si estuvieras en Web.

Puede hacer más que eso, pero eso lo que he probado.

martes, abril 03, 2007

TDD y la matriz de trazabilidad

En MoProSoft y CMMI constantemente nos encontramos con que la matriz de trazabilidad es necesaria para poder medir el impacto que un proyecto tendrá al momento de realizar un cambio. Dicen que es necesario el conocer esto para poder estimar y una vez aprobado el cambio, saber que áreas debes de cambiar.

TDD ofrece un enfoque un tanto distinto. En un principio nos obliga a tener un diseño altamente desacoplado por pensar primero en como usar el código antes de pensar en como hacerlo y principalmente por refactorizar constantemente. Esto nos trae como ventaja que el impacto en distintos componentes de código sea minímo.

Ahora si, partimos de que el impacto es minímo, ¿cual sería la necesidad realmente de tener la trazabilidad si sabemos que pocos componentes se verán afectados?. Por otro lado, gracias a ese diseño desacoplado, ahora sabemos que conocemos los limites y repercusiones del cambio por lo que la estimación es fácil. Hasta aquí cumplimos con el propósito de la matriz de trazabilidad.

La verdad es que ninguno de los dos enfoques es mejor que el otro como para determinar el impacto a los componentes. Algunos dirán que la matriz de trazabilidad da una mejor visibilidad lo cual es cierto, TDD no te la da (no directamente, ya veremos más adelante), pero te da confianza al hacer los cambios. El problema es que tener esa visibilidad viene con un gran costo, el mantener la matriz muy bien actualizada y con alto nivel de detalle, esto sin duda consume mucho tiempo, probablemente más que el necesario para hacer los cambios que pudieran presentarse al proyecto.

Partiendo de que ninguno de los dos enfoques es exacto al preveer el impacto en los componentes afectados, la desventaja de la matriz de trazabilidad es que no es capaz de identificar realmente el impacto que tuvo el cambio una vez realizado y ahí es otra área donde las pruebas que tenemso al usar TDD tienen su alto valor.

Digamos que hacemos un cambio al código y nuestra matriz nos dice que tenemos que cambiar el componente A, C y D, hacemos el cambio en los tres lugares esperados, verificamos que funcionen bien, se integra y antes de liberar aún tenemos que revisar que B no haya sido afectado y probablemente nos daremos cuenta de que B sufrio cambios hasta las pruebas de aceptación y será muy tarde para ver quien introdujo ese error y tratar de corregirlo a tiempo para liberar el producto y volvemos a la fase anterior sin poder liberar.

Viendo el mismo ejemplo en un proyecto que utilizo TDD desde un principio, estamos confiados a que sólo A necesitará un cambio ya que las dependencias entre los otros componentes son muy debiles, realizamos el cambio, damos check in se corre una serie de pruebas automatizadas que nos muestran que B ya no funciona como se esperaba. Ya le habíamos cotizado al cliente sabiendo que sólo tendríamos que cambiar A. En este momento estamos a buen tiempo de para:
a) Corregir el impacto causado en B y volver a dar check in a ver que pasa.
b) Explicarle al cliente que costará más de lo esperado y tomará más tiempo,
c) Que el cliente decida que prefiere si retrasar la entrega del proyecto por su impacto en B o no incluir el cambio propuesto y continuar con la fecha de entrega como se planeo originalmente.

La ventaja de TDD sobre la matriz de trazabilidad es que tiene otras ventajas adicionales a las de poder estimar conociendo el impacto sobre los componentes al hacer un cambio y aunque no nos da visibilidad nos da seguridad que muchas veces es más imporante. Sin embargo, es muy sencillo hacer una pequeña matriz de trazabilidad de Requerimientos-Pruebas-Implementación si se busca cumplir con algún modelo como MoProSoft, CMMI, ISO o simplemente se necesita usar para tener un poco de mejor visibilidad.

¿Cómo hacemos esta matriz usando TDD? Simple, nuestros requerimientos ya los tenemos dados de alta en Team System como Work Items, seguramente como Sprint Backlog Items, Tasks o lo que sea que manejen, un Work Item digamos Requerimiento 1, puede estar relacionado con Tarea 1 y Tarea 2, así que tenemos la trazabilidad directa desde la herramienta de requerimientos a actividades. La actividad usando TDD empezará por realizar una prueba unitaria, por lo que ahora relacionamos Tarea 1 con Tarea1Test y Tarea1FailsTest. Desde las pruebas en código, podemos navegar dando click en "Go to Definition" en el metodo o clase que estamos probando, por lo que tenemos navegación completa de requerimientos hasta implementación. Por otro lado si se tiene una capa de acceso a datos, sabremos claramente que partes de la base de datos se usan y por otro lado esta altamente desacoplado (o debería).

Nos falta otra parte de la navegación, que es desde implementación hacía requerimientos, esta parte es un poco más difícil de seguir, ya que un metodo o clase podría ser usado en diversas áreas, sin embargo dando click en "Find all referencies" llegamos a nuestras pruebas y de las pruebas ya podemos ver que tareas estan relacionadas con esta. Team System no ayuda mucho con la navegación bidireccional entre Work Items pero podrían hacerse algunos queries si fuera realmente necesario.

Resumiendo, con TDD
Tenemos diseños desacoplados que ayudan a minímizar el impacto a los diferentes componentes.
Nos da seguridad y confianza al hacer los cambios sabiendo que tenemos un buen diseño y en caso de errores detectaremos rápidamente si otros componentes se impactaron.
Podemos obtener visibilidad a través de la navegación entre Work Items, Pruebas e Implementación.

sábado, marzo 10, 2007

Como tratar un bug

Todos hemos lidiado con bugs, nuestros o de terceros, en proyectos en desarrollo o en proyectos cerrados, los hemos visto como usuarios de software y tambien como desarrolladores.

Los desarrolladores y los usuarios hemos aprendido que el software no es perfecto, que existen bugs y también service packs. Es cierto que nadie quiere ver esos bugs, pero más importante, nadie los quiere por segunda vez.

Así que aquí estan los 5 puntos de Miguel Madero para lidiarn con bugs (desde el punto de vista de desarrollo).
1. Tener un buen sistema de log.
Lo peor es que el único que puede reproducir el error es el usuario final, pero ¡No sabe como hacerlo!.
2. Revisar frecuentemente ese sistema de log o implementar un sistema de alertas. Los usuarios no van a venir a decirte que errores hay (a menos que sea muy grave) ellos aprenden a vivir con ellos y esperan que algún día se arreglen sólos. Muchas veces ni siquiera estaran concientes de que hay un bug, pero el análizar la información del sistema de log te puede ayudar a corregirlo antes de que se haga costumbre.
3. Habla con tus usuarios.
¿Qué les parece el software? ¿Qué problemas tienen? ¿Qué los hace sufrir cuando usan el software? Estos es muy importante y junto con la informacíón del log puedes encontrar el problema y su causa. Tambien te darás cuenta que muchas cosas que ellos piensan estan mal, simplemente son resultado de una mala capacitación o que no saben como usar cierta funcionalidad. Dependiendo del tipo de sofware se pueden usar tecnicas como reuniones de grupo, foros de discusión, apoyo de usuariso expertos, etc para mitigar esos problemas que por cierto no son el punto de este post.
4. Confirmar que encontramos el bug.
Estan pensando "una vez encontrado, correr a codificar un fix directamente".... NOOOOOOOOOOOOO, no hagan eso (en casos muy sencillos con eso terminaríamos). Hay que estar concientes que hasta este punto sólo hemos encontrado una probable causa y tal vez sólo un posible problema. Lo primero es crear una nueva prueba (vean mi post de TDD) que demuestr el bug. Esta es realmente una de las formas más sencillas de empezar a usar TDD. Para desarrolladores que nunca han usado esta buena práctica, la idea es que antes de codificar creemos una prueba que falle, luego hagamos la prueba pasar, lo cual garantiza que se haya cumplido el requerimiento o parte del requerimiento a probar.
Bueno un bug es finalmente un requerimiento más: "arreglar el problema de....". Así que usamos TDD de la misma forma que con cualquier otro. Aquí es más sencillo porque lo puedes hacer de manera aislada, es decir, no tiene que estar para todo el proyecto, sólo para parte del código, en este caso ya tienes también una idea (gracias al log y la platica con tu usuario) de como hacer una prueba que falle.
Aquí es donde vale la pena algo de código. El bug es el siguiente, el usuario dice que la aplicación esta calculando mal la suma de 2+2. Tu sabes que tienes una clase que hace esa suma, por lo que ese código es el que probaremos.
[TestMethod]
public void TestTwoPlusTwoIsFour()
{
MiClaseSumador sumadora = new MiClaseSumador();
int result = sumadora.Add(2,2);
int expected = 4;
Assert.AreEqual(result,expected);
}

En esta ocasión el log no nos hubiera ayudado mucho. Por otro lado el usar TDD fue muy sencillo en este caso, porque ya existía la clase y metodos a probar. Cuando no se esta acostumbrado a hacer pruebas al estilo TDD lo primero que hechan de menos el intellisense porque cuando escriben algo como "sumadora." el IDE no sabe que metodos hay porque no existen, ni que parametros espera, etc. Bueno cuando estamos corrigiendo un bug nos evitamos este problema haciendo la adopción de la técnica más fácil. Por otro lado, lo que realmente estamos probando antes de codificar no es en sí la clase, sino lo que hace la clase, así que pasamos al punto 5.

5. Corregir el bug identificado.
Una vez que tenemos una prueba que falla, sabemos que ese es el bug y simplemente hay que codificar algo para que pase la prueba. Una vez que la prueba pasa, no sólo estamos seguros de que efectivamente se corrigio el bug, sino más importante aún, de que no aparezca por segunda ocasión (claro hay que volver a correr esta antes de cualquier futuro release del software y mejor aún si se automatiza para que corra con cada checkin o cada noche por lo menos).


Claro que esta es una prueba sencilla, sólo quería ejemplificar. Por otro lado, la mayoría de los bugs podrían requerir de más de una prueba para garantizar que esta corregido, por ejemplo sumar 2,3 o algo distinto.

Otros puntos a notar.
El bug debería quedar documentado, si se usa alguna herramienta como Team Foundation Server el bug deberá relacionarse con las pruebas para que automaticamente nos provea la Trazabilidad que piden MoProSoft o modelos así (que por cierto a veces es útil).
La prueba debe volver a correrse de vez en cuando y mejor aún si es en cada check in.
Es importante el tener una forma rápida de actualizar a los clientes para que este bug quede corregido, si es un website puede ser fácil si es Windows o Mobile algo como Click One o el Updater Agent pueden ayudar.

QUE GANAMOS
Estamos seguros de que lo que correjimos es efectivamente el bug encontrado y no una cosa distinta, la prueba garantiza que realmente podemos reproducirlo.
Estamos seguros de que si se vuelve a presentar, seremos notros los primeros en encontrarlos al correr de nuevo la misma prueba.
Aprendimos TDD
Corregimos el bug.
Trazabilidad del bug a la prueba y a la corrección

viernes, octubre 06, 2006

TDD

Este sera mi primer Post de una serie de publicaciones sobre TDD. Tengo un ejemplo paso a paso de una pequena prueba con esta metodologia, pero antes de meterme a la practica, les dejo un poco de teoria.

http://msdn.microsoft.com/vstudio/teamsystem/tester/default.aspx?pull=/library/en-us/dnvs05/html/GuidelinesforTDD.asp

Estuve revisando mis posts y en todo un mes, hice solo 4 posts. Ya necesito publicar mas. Tengo algunos temas pendientes... vamos a bloggear mucho mas ahora, tanto que parecera spam...