Ventajas de Spock para testear en Java

Imagen de http://www.playbuzz.com/jamesk20/leonard-nimoy-versus-spock-can-you-tell-which-quotes-are-from-his-famous-star-trek-character

Introducción

Groovy es un lenguaje de programación dinámico para la máquina virtual de Java (JVM). Esto hace que sea muy fácil de configurar para alguien con experiencia en Java.
Lo que más destaca de este lenguaje es su sintaxis (syntax sugar) que facilita mucho el desarrollo. Y además es muy fácil comenzar a programar porque lo han hecho muy amigable para la gente que viene de Java. Es casi un superconjunto de Java.

Dentro del extenso ecosistema de Groovy tenemos Spock, que es una framework para escribir test. Spock tiene como ventaja principal la legibilidad de los test. Hace que los test sean muy fácil de entender sin ni siquiera saber nada de Spock o Groovy.
Lo más interesante de Groovy y Spock para alguien que viene de Java es que podemos tener nuestro código fuente en Java y los test en Spock.

Ventajas de Spock

En este apartado se listan los que son a mi entender las mayores ventajas de Spock frente a Java.
En cada apartado hay un ejemplo de como es el código en Spock y en Java para que puedas comprobar de la forma más clara posible la diferencia entre uno y otro. En la parte izquierda de las imágenes aparece el código con groovy y spock y a la derecha el código con java y junit.
Para los ejemplos de java se ha utilizado JUnithamcrest y mockito.

Los ejemplos en Groovy fueron creados por Iván López y los escritos en Java están escritos por mí. En el siguiente repositorio puedes encontrar el código fuente:

https://github.com/kikers25/codemotion-2015-spock

El repositorio lo escribió Iván para una charla sobre Spock y lo he extendido añadiendo los test que aparecen pero en Java porque me parecía que es más fácil ver las ventajas si se ven los dos juntos.

  • Nombre de los test unitarios son cadenas

Los nombres de los test unitarios son importantes para entender que es lo que hace el código del test. En Java, el nombre del test es el nombre del método y no se pueden escribir espacios. Por lo que se tiene que utilizar algún tipo de método para que el lector sepa que hay un espacio. En mi caso suelo utilizar guiones bajos.
Con Spock esto no es necesario porque el nombre del test es una cadena. Así, podemos escribir espacios y otros caracteres que no son posible de utilizar en Java.

 

  • Estructura de cada test en bloques

La separación en bloques de los test escritos en Spock es lo que hace que la legibilidad del test mejore mucho.
Los bloques están basados en el lenguaje gherkin y los bloques más comunes (aunque hay más) que se utilizan en un test son given, when y then. Estos bloques inicializan los objetos a utilizar (given), ejecutan la acción a probar (when) y comprueban que el resultado es el esperado (then).

 

  • Inicialización de clases, listas y mapas

La inicializacion de objetos en groovy es de lo más fácil porque no necesitamos el crear un constructor con todos los métodos, si no que por defecto tiene un constructor al que puedes pasarle en modo de clave y valor todos los atributos que quieres inicializar del objeto. Personalmente utilizo mucho el patrón constructor (builder pattern) para crear objetos para mis tests. Con groovy no es necesario.
Además crear listas y mapas es muy intuitivo y fácil de leer.

 

 

  • Tablas de datos

Con diferencia esta es una de las mejores características de Spock. Añadir test que hacen lo mismo pero con diferentes valores para los parámetros de entrada y el valor de salida utilizando una tabla de datos es una gran funcionalidad. Fíjate en las diferencias en la imagen de abajo.

 

  • Herramienta de dobles de test completamente integrada

Utilizo jmock y mockito para crear mis dobles de pruebas para mis test. Con Spock no necesitas el buscar ninguna librería ya que viene integrada. Lo único que no me gusta es que para que una clase mockeada devuelva un valor no es necesario especificarlo a la clase. Lo que es lo mismo el framework es lenient por defecto.

 

  • Mensajes de error cuando un test falla

Los mensajes de error son más claro que utilizando hamcrest.

Resumen

Groovy en combinación con Spock tienen muchas ventajas a la hora de escribir test si lo comparamos con Junit, Hamcrest y Mockito.
La configuración es muy fácil, resulta fácil el empezar a programar y la legibilidad de los test es muy buena. Es el framework para testing más completo que conozco.

Extra

Si te parece interesante tanto Groovy como Spock y quieres saber más. Los enlaces que aparecen a continuación son vídeos de las charlas de Iván López y Andrés Viedma sobre Spock:

https://www.youtube.com/watch?v=RjH1bd9D2Qs 
https://www.youtube.com/watch?v=D7TjLThg95I

Ejemplo mejorado de doble de test

El artículo anterior trataba de un ejemplo de doble de test de tipo stub.
El stub se llama StubUserDAO e implementa el método findUserBy, devolviendo siempre el mismo objeto.

Si quisiéramos que findUserBy devolviera otro User tendríamos que crear otra clase como StubUserDAO que devolviera ese User, ya que StubUserDAO siempre devuelve el mismo User.

Como puedes imaginarte, no es una solución flexible, ya que casi con cada nueva prueba tenemos que crear una clase parecida a StubUserDAO.

Para solucionar este problema, en este artículo te quiero presentar la versión de un doble de tipo de tipo stub:

La principal diferente, de este doble de test en relación con el anterior es que podemos cambiar el User que va a devolver el método findUserBy.
Podemos cambiar el usuario que devuelve el método utilizando el método.
En el código, el test should_throw_exception_when_passwords_are_different_version1 hace que que StubUserDAOv2 devuelva un usuario con contraseña “other_password”, y el test should_throw_exception_when_passwords_are_different_version2 hace que devuelva un usuario con contraseña “another_contraseña”.

Como he comentado al principio, la principal ventaja de este tipo de stub es flexible ya que permite devolver diferentes tipos de valores sin tener que crear una nueva clase.
Otra ventaja de este tipo de stub es que la configuración del stub para que devuelve el usuario es el propio stub. Al tener la configuración en el stub queda claro lo que estamos probando, porque podemos ver la contraseña del usuario y la contraseña que utilizamos en el signin al mismo tiempo ya que están al lado.
Esto simplifica mucho el comprobar que es lo que fue mal cuando el test falla.

Un par de comentarios sobre el ejemplo:

  • Recuerda inicializar siempre stub. Así nos evitamos desagradables efectos secundarios como que si ejecutas todos los test no todos pasan, pero si ejecutas uno en concreto pasa (también puede ser el efecto contrario). Esto es debido a que un test comparte usuario con otro test a través del stub.
  • Hay que utilizar como atributo en la clase de test el objeto StubUserDAOv2, y no UserDAO. Parece una tontería pero la primera vez que creé el stub, me quede un buen rato atascado porque Eclipse me decía que no podía utilizar el método setUserFoundByEmail y era porque utilicé UserDAO:

 

Ejemplo de código Java hecho con TDD (con historia incluida)

programming challenges
Imagen de http://www.programming-challenges.com/

Hace algunos meses estaba buscando un nuevo trabajo como programador, y una de las cosas que suelen pedir en Holanda, para medir las habilidades de los candidatos, es un ejemplo de código.

Por lo que he vivido durante esos meses, hay un número grande de empresas que no solo quieren un ejemplo de código sino que te piden resolver un problema concreto en un tiempo determinado.

En una de las empresas, el problema que me pidieron resolver me pareció interesante. El problema era el construir una aplicación que calculara la fecha en la que un cliente tendría su pedido listo, teniendo en cuenta la fecha actual del sistema y el tiempo que se tarda en realizar la tarea.

El ejercicio lo hice completamente utilizando TDD y me costó (creo recordar) dos semanas, utilizando horas de mi tiempo libre, ya que tenía trabajo.  Lo tuve que hacer dos veces, debido a que la primera vez, la solución que había elegido en un punto me estaba complicando el añadir más test sin romper los test anteriores.

Después de enviar mi solución al problema, me quedé a la espera de recibir información de si les había gustado, y si era así, si me invitaban a una entrevista. Estaba bastante ansioso de recibir la respuesta porque terminé bastante contento de como me quedó.  No es que sea perfecto el código, sino que era la primera vez que enseñaba mi código a alguien después de haber aprendido TDD.

Nunca recibí ningún tipo de feedback sobre el código y ni siquiera se dignaron en decirme nada de porqué se estaban retrasando tanto.

Ya que no he recibido ningún tipo de feedback y que me quedé con ganas, lo he publicado en GitHub y estaré más que encantado de recibir cualquier tipo de comentario.

No he añadido muchos detalles sobre cual es el problema porque no quiero dar pistas sobre cual era la compañía, pero creo que los comentarios que añadí en el código y los test hacen que resulte fácil de entender.

El enlace al código es:

https://github.com/kikers25/programming_challenge

El ejercicio está hecho con maven y java 7. Las librerías que utilicé son: JUnit 4, hamcrest, Joda-Time y pitest.
Pitest es una librería que mide la cobertura del código utilizando “mutation testing”. Hay un artículo en español muy bueno que explica muy bien qué es “mutation testing”:

http://testeandosoftware.com/mutation-testing/