Como hacer nuestros test más legibles con Hamcrest

0
16072

Como hacer nuestros test más legibles con Hamcrest

0. Índice de contenidos.

1. Introducción

Una de las cosas que he aprendido en la XPWeek 2001, es a hacer más legibles nuestros test de forma muy sencilla utilizando Hamcrest.

Hamcrest es una librería que nos provee de una serie de matchers que podemos utilizar para escribir nuestros test con un lenguaje más cercano al natural de manera que se hace más sencillo comprender que están comprobando nuestros test. Y no solo en java, Hamcrest está portado a C++, Objective C, Python, Php y Erlang.

En este tutorial, vamos a ver como podemos mejorar nuestros test con muy poco esfuerzo utilizando la librería Hamcrest. Por un lado veremos como nuestro código se hace más legible y por otro, veremos como conseguir mensajes de error más descriptivos.

2. Entorno

  • Hardware
    • Mackbook Pro
      • Intel Core i7 2Ghz
      • 8GB RAM
      • 500GB HD
      • Sistema Operativo: Mac OS X (10.7.1)
  • Software
    • Eclipse Indigo
    • JUnit 4.10
    • Hamcrest 1.3.RC2

3. Configuración del entorno.

El primer paso es descargar la librería para hacer uso de ella en nuestros tests. Para ello modificamos el pom.xml agregando las dependencias y dejamos que Maven se encargue del trabajo sucio ;P

La primera dependencia es de JUnit, que nos facilitará la creación de los test a través de anotaciones. Es importante utilizar el artefacto junit-dep para que Maven descarge una versión de JUnit que no traiga Hamcrest. Las dos dependencias siguientes son las relacionadas con Hamcrest, la primera trae los matchers más básicos y la segunda dependencia trae una graaaaan cantidad de matchers.

4. Ejemplos

Como veis, pasamos directamente a los ejemplos porque como ya decía en la introducción del tutorial, el uso de la librería es muy sencillo y seguro que en seguida le cogéis el truco ;).

La idea es mostrar primero como se hace solo con Junit y a continuación mostrar como se hace con Hamcrest. Quizás los test que se van a hacer no tengan mucho sentido, pero el objetivo del tutorial es ver como usar la librería. Una vez que hayáis visto los ejemplos, seguro que no os es dificil extrapolar los ejemplos a vuestro entorno de test :).

Siguiendo el símil del fútbol del tutorial de nuestro compañero Miguel, vamos a tener un equipo de fútbol con varios jugadores y vamos a realizar unos cuantos test sobre el equipo de futbol.

NOTA: Como la clase Jugador va a formar parte de una colección, es buena práctica redefinir equals y hashCode.

Primer ejemplo

Vamos a ver un primer ejemplo muy sencillo.

La primera diferencia es que en vez de usar los diferentes asserts de JUnit como pueden ser assertNotNull, assertTrue, assertEquals, etc, usamos assertThat (que es de JUnit). ¿Que conseguimos con esto?, conseguimos una primera mejora de legibilidad al leer el test. Con JUnit sería algo como «afirma no nulo equipo» y con Hamcrest «afirma que equipo es no valor nulo».

Vamos a explicar un poco más la línea de Hamcrest «assertThat(equipo, is(not(nullValue())));», La gracia está en el segundo parámetro de la función «asserThat» en donde hemos usado una serie de matchers de Hamcrest para crear nuestro «assert». El primer matcher es is, que solo se usa por temas de legibilidad, se podría haber escrito otro assert sin usar el matcher is con la misma funcionalidad. El segundo matcher es not que sirve para la negación lógica del resultado de evaluar la expresión que le pasamos como parámetro. El tercer y último matcher es nullValue que sirve para comprobar, como su nombre indica, si el valor es «null».

Otra mejora que obtenemos con el uso de Hamcrest es el mensaje descriptivo que nos devuelve en caso de que no se cumpla un assert. Si por ejemplo hacemos fallar el test anterior en el assert de JUnit y luego en el assert con Hamcrest obtenemos estas salidas:

Mensaje de error sin Hamcrest

Mensaje de error con Hamcrest

Sobre los mensajes de error, es cierto que a los assert de JUnit se le puede poner como primer parámetro un mensaje de error descriptivo, pero personalmente no me gusta esa opción, ya que si más tarde modificamos el test (aunque en principio una vez hecho un test, éste no se debería de modificar) además tendremos que modificar los comentarios. Y no solo por tener que mantener la documentación, si no que si por algún casual se nos olvida modificar el comentario del test y el test falla más adelante, puede ser muy difícil descubrir la causa del error, ya que el mensaje que nos estará mostrando no se correspondería con la realidad.

Segundo ejemplo

A continuación os muestro otro ejemplo de uso de Hamcrest

Igual que en el caso anterior, mejoramos a la hora de leer el código. En el primer caso sería algo como: «afirma falso equipo dame jugadores es vacío». En el segundo caso sería algo como: «afirma que equipo dame jugadores es vacío es falso.» Quizás mi libre traducción 😛 no sea la más clara pero bueno, seguro que cuando leéis el código notáis que es algo más fácil de comprender.

Mensaje de error sin Hamcrest

Mensaje de error con Hamcrest

Mensaje de error con Hamcrest, 2º opción

Tercer ejemplo

El contenido del tercer test de ejemplo es el siguiente:

En este test estamos usando el matcher hasItem para comprobar si en nuestro equipo tenemos un portero, o lo que es lo mismo si en la lista de jugadores existe un item con la posición portero. La lectura con JUnit es algo como «afirma true equipo dame jugadores contiene portero». La lectura con Hamcrest es «afirma que equipo dame jugadores tiene item portero». Quizás en este caso, la mejora no sea mucha, pero ahora veremos como en el ejemplo siguiente como la legibilidad mejora cuando comprobamos con varios items.

Mensaje de error sin Hamcrest

Mensaje de error con Hamcrest

Como podemos ver en el mensaje de error con Hamcrest, se esperaba que en la colección de jugadores de AutentiaFc hubiese un entrenador, pero no lo hay 🙂

Cuarto ejemplo

En este caso, si se nota una mayor mejoría al usar Hamcrest. Si hacemos como en los casos anteriores e intentamos leer el código tenemos que el ejemplo de JUnit sería: «afirma true equipo dame jugadores contiene portero equipo dame jugadores contiene defensa equipo dame jugadores contiene delantero». En el segundo caso sería «afirma que equipo dame jugadores tiene items portero defensa delantero».

Mensaje de error sin Hamcrest

Mensaje de error con Hamcrest

En este caso hemos conseguido aumentar la legibilidad del código y obtener un mensaje más descriptivo en caso de que no se verifique el test.

Quinto ejemplo

En este caso, la única diferencia con el anterior es el uso del matcher anyOf, que lo podemos utilizar cuando que se cumpla alguna de las condiciones que le pasamos por parámetro. Es equivalente al operador boleano «||» de Java. También existe el matcher allOf que es el equivalente al operador boleano «&&».

5. Construir nuestro matcher

Existen una gran cantidad de matchers que podemos usar, sobre todo si hemos importado hamcrest-library, pero puede resultarnos interesante implementar el nuestro.

Hamcrest nos provee de una clase de la que heredar para construir nuestro matcher. La clase es TypeSafeMatcher, que nos obligará a implementar dos métodos: public boolean matchesSafely(T item) y public void describeTo(Description description);. En el primer método haremos las comprobaciones que necesitemos y en el segundo escribiremos el mensaje de error en caso de que no se cumpla el test.

Nuestro matcher va a recibir un equipo, en este caso AutentiaFC, que va a comprobar que el equipo sigue una formación 4-4-2.

La implementación quedaría así:

Además se ha añadido el método tacticaCuatroCuatroDos que nos construirá el matcher cuando lo necesitemos desde el test. La anotación @Factory sirve para que utilidades puedan reconocerlo. Hamcrest viene con una utilidad que reconoce estas anotaciones para que nos cree una única factoria con todos nuestros custom matchers de tal modo que sólo necesitemos un único import static en nuestro test.

Para poder usar nuestro matcher sólo tenemos que incluir el método de factoría. Una vez incluido, ya podemos usar nuestro matcher en un assertThat :D.

Si provocamos el fallo quitando un delantero a nuestro equipo, tenemos un error como este 🙂



6. Conclusiones

Hemos visto como podemos hacer nuestros test más legibles con muy poco esfuerzo usando Hamcrest. Y si no nos fuese útil ningún matcher de la gran cantidad que trae, vemos que es muy sencillo crear los nuestros, de forma que se ajusten perfectamente a nuestras necesidades.

Para cualquier comentario, duda o sugerencia, tenéis el formulario que aparece a continuación.

Un saludo.

Dejar respuesta

Please enter your comment!
Please enter your name here