xUnit Test PatternsSe dice que el mayor aliado de los controles de calidad son las pruebas automatizadas, en esta nota nos vamos a dedicar a hablar un poco de ello.
Algunos de los objetivos de las pruebas automatizadas, que luego detallaremos, son los siguientes:

  • Las Pruebas nos deben ayudar a aumentar la calidad
  • Las pruebas nos deben ayudar a comprender el sistema que está siendo probado
  • Las pruebas deben reducir (y no introducir) el riesgo
  • Las pruebas deben ser fáciles de ejecutar
  • Las pruebas deben ser de fácil lectura y de dar mantenimiento
  • Las pruebas deben requerir un mantenimiento mínimo, así como el sistema probado

Las pruebas nos ayudan a mejorar la calidad

La razón principal de hacer pruebas es garantizar que las cosas estén funcinando según lo solicitado, asegurando así la calidad.
La garantía de la calidad se puede lograr debido a que las pruebas nos ayudan a actuar de la siguiente manera:

Prueba como especificación o especificación ejecutable
Al escribir las pruebas antes de la codificación (la prueba es parte del desarrollo), las pruebas nos darán una idea de lo que el sistema debe hacer, antes de empezar a hacerlo realmente.

Las pruebas nos permiten especificar el comportamiento del sistema en diferentes escenarios. Siendo así, una especificación ejecutable de lo que el sistema debería hacer. Garantizamos así que hemos construido el sistema de forma correcta (según lo especificado), conforme las pruebas pasan, usted puede asegurarse de que las pruebas reflejan la forma en que el sistema debería funcionar.

Repelente a los errores
Las pruebas encuentran los errores existentes, pero no es sólo eso. Las pruebas automatizadas evitan que los errores sean introducidos. Sin embargo, para que las pruebas funcionen de esta manera, ellas deben ejecutarse cada vez que un check-in, o commit se hace.

Localización de defectos
Ocurren errores. Claro, algunos son mucho más difíciles de arreglar que otros. Mediante la realización de pruebas unitarias podemos identificar y corregir estos errores con mayor rapidez, por lo que una prueba falla. Esta es una ventaja que las pruebas unitarias tienen sobre las pruebas de aceptación. Cuando una prueba de aceptación falla, significa que algún comportamiento esperado no está funcionando como fue solicitado. La prueba unitaria nos dice porque este comportamiento esperado no está funcionando.

Todos estos beneficios son excelentes, y nos los ganaremos si no escribimos las pruebas pra TODOS los posibles escenarios que las pruebas unitarias precisan cubrir. Y es evidente que no tendremos ningún beneficio si el propio código de pruebas tiene errores.


Las pruebas deben ayudarnos a comprender el sistema

Bloquear los errores y sus consecuencias no es la única cosa que las pruebas pueden hacer por nosotros. Ellas pueden dar leyendolas lo que el código debería hacer. Los componentes de las pruebas son en realidad una descripción de los requisitos de los componentes de software.

Pruebas como documentación
Sin las pruebas automatizadas, habría que estudiar cuidadosamente cada pieza de código tratando de responder a la pregunta: "¿Cuál debe ser el resultado si ...?" Ya con la prueba automatizada, nosotros simplemente usamos nuestras pruebas como documentación. Ellas nos dicen qué resultados se producen si ... Y si queremos saber cómo el sistema hace alguna cosa, basta con activar el debug (depurador), ejecutar la prueba, y seguir paso a paso el código para ver cómo funciona.
En este caso, las pruebas sirven como documentación.
Las metodologías ágiles predican que si un código tiene una buena cobertura de pruebas no hay necesidad de documentación, basta conocer las pruebas y usted conoce el sistema probado.

 

Las pruebas deben reducir (y no intruducir) riesgo

Como se mencionó antes las pruebas deberían aumentar la calidad de nuestro software ayudandonos a obtener una documentación mejor de los requisitos y prevenir que los errores sean insertados durante el desarrollo incremental. Esto es claramente una reducción de los riesgos. Otra forma de reducir el riesgo requiere verificar el comportamiento del software en circunstancias "imposibles" cuando hacemos las pruebas normales de los clientes.

Es muy bueno hacer charlas con su equipo de los tipos de riesgos que pueden eliminarse o reducirse mediante el uso de pruebas automatizadas.

Pruebas como red de seguridad

Las pruebas automatizadas son para el programador una red de seguridad tal cual como para un trapecista, la red de seguridad proporciona la libertad para un trapecista de intentar un nuevo salto o una figura diferente, porque si él comete un error la red de seguridad estará allí para salvarlo.
Las pruebas automatizadas funcionan de la misma manera, el programador puede arriesgar con una refactorzación o una mejora en el código, porque si alguna cosa cambia en el comportamiento esperado del programa, las pruebas lo dirán. Esta es una excelente manera de prácticar el coraje que XP y otras metodologías ágiles predican.
La eficacia de la red de seguridad depende de una costura bien hecha, no debe haber agujeros, en las prueba los agujeros en la red de seguridad son causados por la falta de cobertura de las pruebas. Y falta para todas las condiciones de las pruebas son como líneas débiles en la costura.


Las pruebas deben ser fáciles de ejecutar

La mayoría de los desarrolladores sólo desea escribir código, la tarea de probar la funcionalidad es una carga para ellos. Las pruebas automatizadas proporcionan una red de seguridad a fin de que podamos escribir código con mayor rapidez, y ejecutemos las pruebas con frecuencia si ellas son realmente fáciles de ejecutar.
Pero que hace que una prueba sea fácil de ejecutar? Esta es una pregunta con una respuesta en cuatro factores:

  • Las pruebas deben ser completamente automatizadas, lo que significa que ellas debe ejecutar sin que nadie tenga que hacer algún esfuerzo.
  • Las pruebas deben ser auto-chequeables, eso significa que ellas deben saber todas las salidas del programa, saber si están pasando y, de preferencia, enviar un informe a alguna de las partes interesadas en los resultados.
  • Las pruebas deben ser repetitivas, lo que significa que deben ser ejecutadas varias veces sin ningún cambio en sus resultados.
  • Lo ideal sería que las pruebas deben ser independientes de otras pruebas para que ellas ejecuten solas.

Esto asegura a los desarrolladores la tranquilidad de ejecutar las pruebas en cualquier momento a todos, en todo commit, y tener nuestras pruebas como una red de seguridad.


Las pruebas deben ser fáciles de ejecutar y mantener

Codificar es una actividad difícil porque tenemos que mantener una serie de informaciones en nuestras cabezas. Y cuando escribimos pruebas debemos centrarnos en probar y no codificar las pruebas. Esto significa que las pruebas deben ser simples, fáciles de leer y para escribir tambien.
Uno de los problemas es que en las pruebas en general, intentamos probar mucho en una sola prueba, otra dificultad es expresar lo que quieres poner a prueba en el lenguaje que está utilizando. A continuación le indicamos cómo hacer su prueba más fácil de ejecutar y mantener:

Pruebas simple
Nuestras pruebas deben ser pequeñas y sólo probar una cosa a la vez. Nada de morder más de lo que puede masticar. Esto es sumamente importante, especialmente cuando se utiliza TDD, ya que el código debe ser escrito para pasar una prueba por vez, y es importante que cada prueba insidirá apenas en un nuevo comportamiento del sistema probado. Para separar la prueba se recomienda el uso de un método para cada condición de prueba .

Pruebas expresivas
Como vimos anteriormente las pruebas sirven como documentación, por lo que las pruebas deberían expresar claramente la funcionalidad que ellas quieren probar.
Una forma de dejar sus pruebas bien claras, lo que tiene que hacer es utilizar métodos utilitarios que sean como parte de la "burocrácia" en la prueba y que los métodos de prueba, estén solamente las acciones a ser probadas.

Separación de tipos
Tenemos que mantener nuestro código de prueba separado del código del sistema de producción y otra separación debe estar es nuestros propias pruebas.
Esta separación de tipos nuestras pruebas es hecha para no mezclar lo que se está poniendo a prueba, por ejemplo, la lógica de negocio de las pruebas de interfaz, e incluso las pruebas unitarias.
Esto debería hacerse por separado porque si cambia la interfaz no es necesario preocuparse por las pruebas de lógica de negocios y viceversa.


Las pruebas deben exigir un mantenimiento mínimo, así como el sistema probado

El cambio es algo normal en nuestras vidas. Deberíamos escribir para hacer las pruebas automatizadas para hacer los cambios en el código más fácil, por lo que debemos tener cuidado para que nuestras pruebas no hagan más difícil los cambios en nuestros códigos.
Un buen ejemplo es el cambio en la firma de un método en una clase, si usted añade un nuevo parámetro a un método y de repente 50 pruebas empiezan a fallar, no nos me sentiríamos con animó de cambiar otro método. Una sugerencia para eludir este problema es crear un nuevo método que sume el nuevo parámetro, y refactorizar el viejo método llamando al nuevo método, pasando el valor por defecto para el nuevo parámetro.

Pruebas robustas
Inevitablemente vamos a hacer muchos cambios en el código de nuestros proyectos, porque los requisitos por lo general cambian con el tiempo. Por lo tanto, queremos escribir las pruebas de manera que pocas casas en ellas se vean afectadas después de un pequeño cambio en el sistema que estamos probando.
Esto significa que debemos eliminar la dependencia entre nuestras pruebas, también debemos garantizar que los cambios en el entorno de pruebas no afectan a nuestras pruebas, hacemos esto asilando el sistema probado del ambiente tanto como sea posible, esto resulta en pruebas mucho más sólidas y robustas.

 

Un muy buen libro para leer es "xUnit Test Patterns-Refactoring Test Code" de Gerard Mészáros, un libro lleno de pautas, consejos sobre cómo llevar a cabo las pruebas, que no hacer y cómo mejorarlas.

Inspiración.

"Si tú tienes una manzana y yo tengo una manzana e intercambiamos las manzanas, entonces tanto tú como yo seguiremos teniendo una manzana cada uno. Pero si tú tienes una idea y yo tengo una idea, e intercambiamos las ideas, entonces ambos tendremos dos ideas"

Bernard Shaw