Un ejemplo de doble de test con stubs

Stub
Imagen de http://www.parts-recycling.com

Introducción

Un doble de test simula un objeto de producción y nos permite el probar otros objetos.

Según Martin Fowler en este artículo, hay cinco tipos de dobles de test: dummy, fake, stubs, spies y mocks.

En este artículo voy a enfocarme en stubs y en fakes dummies porque son los dobles de test que más suelo utilizar.
Para saber más sobre los dobles de test podéis leer el artículo de Fowler o este de XUnitPatterns, que son muy buenos.

Aunque podemos crear un objeto de tipo stub con cualquier framework de pruebas como JMock o EasyMock, es mejor hacerlo nosotros mismos para así entender mejor como funcionan.

Ventajas de los dobles de test

Los dobles de test unitarios tienen dos ventajas frente a utilizar los objetos reales de producción:

  • Determinismo: siempre se va a comportar como queremos. Con los objetos reales dependiendo de muchos factores van a comportarse de una forma u otra. Así, si el objeto accede a base de datos se comportará de forma distinta dependiendo de los registros que hay en la base de datos.
  • Rapidez: no acceden a la base de datos o al sistema de ficheros y tampoco acceden a Internet por lo que hacen que el test sea mucho más rápido, que es lo que queremos. Porque unos de los principales cualidades de un buen test unitario es que es rápido porque así podemos ejecutarlo a menudo.

 

Stubs

Stubs, cuya traducción al español no me atrevo a hacer, son objetos que han sido programados de forma muy simple por nosotros para que responden de una forma determinada.

Imagina que tenemos un objecto UserService que tiene un método que se llama signIn que permite validar que un usuario pueda hacer “log in” y devuelve el usuario.

En el código anterior, el método signIn solo hace tres cosas: buscar al usuario a partir de su email, validar que las contraseñas son las mismas y devolver el usuario.

Lo que queremos probar es que si el usuario tiene diferente contraseña que la pasada por parámetro se va a lanzar una excepción de tipo AuthenticationException.
Para ello, queremos que UserDAO devuelva un usuario con diferente contraseña que la que pasamos por parámetro.
Vamos a crear una clase de implemente UserDAO y cuyo método findUserBy devuelva siempre el mismo usuario.

El nombre del objeto de tipo stub es StubUserDAO y como puedes comprobar no tiene ningún misterio. Es una clase que implementa UserDAO cuyo único método devuelve un usuario con la contraseña “other_password”.

El método initialization crea los objetos UserService y StubUserDAO e inyecta el objeto StubUserDAO a UserService.

En el ejemplo, hay dos test unitarios que hacen lo mismo pero de dos formas distintas. Ambos comprueban que se lanza la excepción AuthenticationException.

El test should_throw_exception_when_passwords_are_different_version1 comprueba que se lanza la exception con un fail cuando no se ha lanzado.
El test should_throw_exception_when_passwords_are_different_version2 lo comprueba a través del estándar JUnit.

Dummy

Dummy es otro tipo de doble de test que no hace nada. Se suele utilizar para no tener que pasar como parámetro un null como valor.

En el ejemplo anterior el objeto EMAIL es un doble de test de tipo dummy. El código de producción que implementa la interfaz UserDAO lo utiliza, pero como estamos utilizando nuestro stub pues no se utiliza.
En el ejemplo, si EMAIL valiera null no pasaría nada pero eso es porque no es código real de producción.
Suelo comprobar que los parámetros (email y password en este ejemplo) del método no valgan null en mi código de producción.