Injección de dependencias en Java para dummies

Introducción

Cuando se habla de test unitarios siempre se acaba hablando de inyección de dependencias pero qué es y porque es tan importante cuando queremos probar nuestro código.

Qué es

Como comenté en el artículo sobre mis primeros pasos con test unitarios, la inyección de dependencias consiste en pasar a un objeto sus objetos colaboradores, es decir, los objetos que utiliza.

 

Diagrama 1: Código de producción

 

En la diagrama anterior podemos ver una clase UserService que permite validar que un usuario puede logearse utilizando el método signIn.
Para obtener la información del usuario a partir de su email necesita utilizar la clase UserDAOOracle.

 

Diagrama 2: Código de prueba

 

En este segundo diagrama podemos ver la clase UserService que hace lo mismo que en el diagrama anterior pero que para obtener la información del usuario utiliza FakeUserDAO.
Esta clase FakeUserDAO la hemos creado nosotros para controlar el resultado que devuelve y así no tener que depender de la base de datos.

Para poder pasar del primer diagrama al segundo tenemos que hacer algo para que la clase UserService pueda utilizar la clase FakeUserDAO en vez de UserDAOOracle, y ese algo es la inyección de dependencias.

Porqué

La inyección de dependencias nos permite que nuestro código sea más flexible ya que hace que una clase pueda cambiar sus colaboradores sin necesidad de cambiar su código.

En relación a los test unitarios, la inyección de dependencias permite que nuestros test unitarios sean repetibles (puedes ver las propiedades de un buen test unitario aquí).

Un test es repetible cuando el resultado es siempre el mismo: pasa (verde) o no pasa (rojo).
Si el test no fuera repetible no confiaríamos en su resultado y no nos sería de utilidad.

La inyección de dependencias hace que nuestros test sean repetible ya que nos permite controlar los colaboradores.

Así, usamos los objetos reales cuando estamos en el código de producción y objetos que podemos manipular (dobles de test) cuando estamos con los test unitarios.

Cómo

Para que FakeUserDAO pueda sustituir a UserDAOOracle debe de tener la misma API, una serie de métodos públicos idénticos.
La forma habitual de hacer eso es crear una interfaz con los métodos que tengan que implementar ambas.
Así, en este caso podíamos crear una interfaz UserDAO e inyectar UserDAO a UserService:

 

Hay cuatro formas principalmente de inyectar dependencias:

  1. En el propio método: Añadir la dependencia en el método que la utiliza. Así, en nuestro ejemplo  añadiríamos la dependencia en el método signIn.
  2. Constructor: como su nombre indica añadimos las dependencias en el constructor
  3. Método o Setter: Se puede crear un método por cada dependencia o uno para todas.
  4. Campos públicos: Las dependencias son campos públicos que son declarados en el constructor por lo que podemos cambiarlos.

Para no hacer el artículo muy largo vamos a ver un ejemplo del método que suelo utilizar habitualmente, que es el constructor.
Suelo utilizarlo porque creo que es el más simple ya que si no creamos el constructor por defecto (sin parámetros) al crear una instancia el compilador nos obliga a utilizar el constructor con las dependencias, por lo que no necesitamos recordar como inyectar las dependencias.A continuación podemos ver el código de la clase UserService y como sería el código en producción y el de test:

Podemos ver que en el código de producción utilizamos UserDAOOracle y que en el código de prueba utilizamos FakeUserDAO debido a que ambas implementan la interfaz UserDAO.
El método UserService.signIn solo lanza una excepción cuando no se ha encontrado el usuario utilizando el email de este.Quería haber simplificado al máximo el código pero no me gusta nada devolver null en ningún método y por eso lo que he hecho es devolver un objeto NoUser, que hereda de User y que sobrescribe el método isEmpty devolviendo siempre true:

 

Tips

Algunos pequeños consejos que me hubieran gustado escuchar cuando empecé:

  • Si no sueles inyectar dependencias empieza a hacerlo ahora mismo, aunque no creas sus test unitarios. Sin inyectar dependencias es mucho más complicado probar tu código.
  • No utilices static, ni clases que implementen el patrón Singleton porque te va a resultar más complicado el inyectar las dependencias y crear test unitarios. Hay algunas excepciones pero son muy pocas.
  • Si tienes que inyectar más de dos dependencias en una clase puede ser un problema de diseño. Comprueba si la clase no está haciendo más de una cosa a la vez y está incumpliendo el principio de única responsabilidad.
  • Cuando quieras crear test unitarios de clases existentes y que no tengan inyectadas las dependencias sin romper nada creo que el siguiente enlace que contiene un video de Sandro Mancuso te puede ayudar:

http://craftedsw.blogspot.nl/2012/12/screencast-testing-and-refactoring.html

¿Conoces algún consejo más sobre la inyección de dependencias?

Modificado 17-05-2014: Después de leer este artículo de Carlos Ble he decido cambiar el nombre del método UserDAO.findUserByEmail a UserDAO.findUserBy porque es cierto que es redundante al pasar como parámetro un objeto de tipo Email.

Test unitarios con fechas en Java

Introducción

En el artículo anterior hablé de forma general de como comencé a crear test unitarios en Java.
En este quiero hablar en específico de crear test unitarios cuando el código a testear utiliza fechas.

Manejar fechas en Java es complicado (al menos hasta la versión 7) pero es incluso peor el tema cuando intentas crear test unitarios para tu código.

El problema principal de manejar fechas en Java es debido a que la clase Date, que es la que se suele utilizar habitualmente, es mutable. Es decir, que se puede modificar su valor interno después de haber sido inicializada.
El otro problema es el como crear test unitario con fechas si utiliza como referencia la fecha actual del sistema.

Contexto

Como en el artículo anterior he utilizado en el código las librerias Junit v4 y harmcrest. Podéis encontrarlas en:

http://junit.org/
http://hamcrest.org/JavaHamcrest/

Mutable?¿

Te preguntarás cual es el problema que los objectos de tipo Date sean mutables.
El tema es que podemos modificar el campo fecha de un objeto sin que este pueda hacer nada.

Vamos a verlo en un ejemplo. Tenemos un clase User con un campo con la fecha de nacimiento de tipo Date:

En el siguiente código podemos ver en un test unitario cómo puede afectar la mutabilidad a la clase User:

Hemos cambiado el año de la fecha de nacimiento del usuario de 1990 a 2014 sin que el objeto user lo sepa.

Formas de probar código con fechas

Hay principalmente tres formas de crear métodos testeables que contengan fechas:

  • 1. Pasar la fecha actual como parámetro. De esta forma resulta muy fácil el cambiar la fecha actual del sistema y así probar todos los posible casos que queramos.
  • 2. Obtener la fecha actual por un método que podamos cambiar. Dentro del método que queremos probar, obtenemos la fecha actual del sistema por un método (en la misma clase) con el scope protected para que en el test unitario podamos sobrescribir el método utilizando una clase que extienda ese método.
  • 3. Utilizar nuestra propia clase como fecha del sistema. Dentro del método que queremos probar, utilizamos una clase propia para obtener una instancia de la fecha actual del sistema.

Creo que la mejor forma de ver como funcionan estas tres formas de probar código con fechas es utilizando el mismo problema y ver como es el código en las tres aproximaciones.

El problema: ¿es mi cumpleaños hoy?

Tenemos la misma clase User que utilizamos al principio del artículo:

y queremos probar el método isBirthday (no está implementado en el código anterior porque es diferente según la aproximación). Esté método comprueba si el usuario cumple años hoy y devuelve un boolean con el resultado.

Si para obtener la fecha actual del sistema utilizamos:

new Date()

El código no sería testable porque no podríamos cambiarla.
Nota: Esto es solo un ejemplo de como probar código con fechas. Personalmente no suelo probar cada método de forma individual sino que pruebo el comportamiento del sistema.

Primera aproximación: pasar la fecha actual como parámetro

Primeramente veremos la estructura del método a probar y posteriormente el código de los test unitarios. Así el método isBirthday sería como aparece a continuación:

La fecha actual (now) se ha pasado como parámetro, luego se comprueba los dias y meses de los objetos now y dayOfBirthday de tipo Date (se ha omitido esa parte por innecesaria en el ejemplo) y posteriormente se devolverá true o false según estas comprobaciones.

A continuación dos test unitarios que comprueban los dos casos básicos del método, que el usuario cumple años hoy y que no cumple años hoy:

Las variables five_july_2000, six_july_2010 y five_july_2010 son constantes de tipo Date que como no aportan nada al ejemplo he eliminado su definición.

 

Segunda aproximación: obtener la fecha actual con un método que podamos cambiar

Seguimos con el mismo problema pero como obtenemos la fecha actual de un método no necesitamos pasarlo como parámetro. Así, el código del código de producción es:

Los mismos dos test unitarios de la aproximación anterior son implementados en el siguiente código con el nuevo método:

Lo que estamos haciendo en ambos test unitarios es crear una clase anónima que extiende de la clase User, donde está sobrescrito el método getDate con la fecha actual que deseamos.

 

Tercera y última aproximación: Nuestra propia clase fecha del sistema

Esta tercera forma de probar código con fechas la leí en el libro Test Driven de Lasse Koskela y es la que suelo utilizar habitualmente.
Podéis encontrar el código fuente de esta junto con todo el código fuente del libro en la siguiente dirección:

http://www.manning.com/koskela/

El código de producción sería:

La clase SystemTime que hemos utilizado para obtener la fecha actual, es parte de esta tercera aproximación, y consiste en una clase que genera la fecha actual a partir del valor en milisegundo de una instancia hija de la interfaz TimeSource:

que por defecto tiene el valor System.currentTimeMillis() pero que puede ser modificada si le pasamos a SystemTime otra instance hija que implemente la interfaz TimeSource.

El código que nos interesa de la clase SystemTime es:

Así los dos test unitarios quedarían de la siguiente forma:

Como puedes comprobar aunque los test quedan bastante simples, pueden afectar al comportamiento de otros test unitarios que utilicen la clase SistemTime para obtener la fecha actual ya que no será la real.

Para solucionarlo tenemos que asignar a SystemTime su valor por defecto. Para ello, en el método que se ejecutar después de cada test unitario lo añadimos:

 

Ejemplo de donde utilizar estos métodos

Hay un caso en particular que me está viniendo muy bien el poder cambiar la fecha del sistema, y es cuando pruebo procesos en segundo plano.

En general los procesos en segundo plano siempre tienen un campo de tipo Date que la forma de probar que funciona es manualmente: esperar hasta cierto día/hora en concreto para comprobar que ha funcionado como debía.
Si podemos crear test unitarios cambiando la fecha del sistema tendremos más seguridad que nuestro proceso funciona.
Esto no quita que se pueda probar manualmente que el proceso funciona.

 

Conclusiones

Aunque personalmente la aproximación que más me gusta es la última creo que mientras el código sea testeable cualquiera vale.

Modificado 08-01-2017: Cambiado formateo del código a GitHub

Mis primeros pasos con test unitarios en Java

Definición

Un test es una prueba que se realiza sobre el código de forma automática. Y la mejor definición que he encontrado sobre qué es, o mejor dicho qué no es, un test unitario es de Michael Feather (@mfeathers):

Un test no es unitario cuando:

  • Habla con la base de datos
  • Se comunica a través de la red
  • Toca el sistema de ficheros
  • No puede ejecutarse al mismo tiempo que tus otros test unitarios
  • Tienes que hacer cosas especiales para ejecutarlo

Después de esta definición, para mí un test unitario es un test que se ejecuta en pocos milisegundos ya que en definitiva el listado anterior lo que hace es describir los casos que hacen que el test tarde en ejecutarse.

 

Cómo funciona

Ejecutas el test unitario en tu IDE favorito (Eclipse, Netbeans, IntelliJ) y si tu código pasa el test vas a ver el nombre de tu test en verde, y si no lo pasa será rojo.

 

Qué utilizo

Para crear test en Java utilizo las librerias JUnit 4  y Harmcrest.

JUnit es el estandar para crear test en Java y no conozco ninguna otra alternativa. Utilizo la versión 4 porque permite identificar los test utilizando anotaciones Java: @Test.

Harmcrest lo utilizo porque hace que se entienda mejor el test y cuando el test está en rojo los mensajes de error son más claros, pero no es necesario utilizarlo para escribir un test.

 

Ejemplo

La primera línea es una anotación de JUnit 4 que identifica el método que aparece después de la anotación como un test.

A continuación aparece un método que no devuelve ningún valor y cuyo nombre describe lo que comprueba el test. El nombre del método es muy importante porque sirve como documentación para el programador.

El contenido del método está escrito utilizando la libreria Harmcrest. El método assertThat tiene dos parámetro. En el primero, ponemos lo que queremos comprobar y en el segundo, el valor que debe tener.
Los dos objetos deben de ser del mismo tipo, por ejemplo, en este caso son dos String.

 

Por qué escribir test

En mi caso empecé a escribir test unitarios por básicamente dos motivos:
  1. TDD: Quería probarlo y hacer test unitarios me parecía el primer paso.
  2. Menos testing manual: No me gusta hacer pruebas manuales y todo lo que pueda reducir el tiempo que le dedico a ello pues mejor que mejor. No quiero decir que no las haga, sino simplemente que no me gustan.
Al ir escribiendo test descubrí otros dos motivos que son incluso mejores que los dos anteriores:
  1. Seguridad/Confianza al cambiar el código: Principalmente al cambiar el código escrito hace un tiempo. No se cuantas veces en el pasado habré cambiado un método de una clase para solucionar un error y posteriormente me dí cuenta que añadí errores en otro lado del código. Con test unitarios si rompo algo el test me avisará.
  2. Documentación para programadores: Los test unitarios se convierten en documentación y cuanto mejor son los nombres de los test, y el test en sí, mejor es la documentación. Si necesito utilizar un método y no sé como funciona suelo mirar los test unitarios de este para averiguarlo.

 

Al meollo: Como comencé

Cuando comenzé a escribir test unitarios me dí cuenta que no sabía como escribirlos en muchos casos. Por ejemplo:

  • Servlets
  • JSP
  • Comunicación con internet
  • Comunicación con la base de datos
  • Comunicación con el sistema de ficheros
  • Fecha y hora del sistema

En muchos de estos casos si consigo crear un test no sería unitario porque como vimos en la definición harían que el test tardara en ejecutarse. Además que tendría que configurarlos de alguna forma para hacer que se comportaran como quiero.

Estuve pensando sobre estas limitaciones y como no supe como solucionarlo llegué a la conclusión que lo mejor que podía hacer era evitar el problema: aislar la lógica de mi sistema en métodos que no accedieran a nada de lo anteriormente citado y que tuvieran unos valores muy claros de entrada y salida.

Si, por ejemplo tenemos una función que accede a base de datos para obtener una lista de usuarios y luego los agrupa por departamentos creaba un método cuya entrada fuera la lista de usuarios y cuya salida es la lista de departamentos:

Para el tema específico de las fechas, lo solucioné pasando siempre la fecha actual como parámetro de entrada del método.

Así empecé a crear test unitarios de funciones relativas a fecha o que tuvieran una entrada y salida bien definidas.

Nota: sé que la mejor forma de verlo es por ejemplos. Crearé un proyecto en GitHub junto con al menos un post explicando el código.

 

Problemas que he encontrado

Han sido dos los problemas principales que he encontrado y ya los he comentado en el punto anterior aunque de forma específica:

  1. Probar código en tecnología propia de Java: JSP, Servlets, Hibernate, JDBC y otros frameworks.
  2. Probar código que se comunica con recursos: Base de datos, internet y sistema de ficheros.

Las soluciones al primer punto están en la primera parte del libro Test Driven de Lasse Koskela (@lassekoskela):

http://www.manning.com/koskela/

Además, en el libro se dan las claves para practicar TDD y ATDD. Así que hay que a comprarlo.

Para el segundo punto tengo tres palabras que suelen solucionar la mayoría de estos problemas:

Inyección de Dependencias

Siempre que una clase utilice métodos de otra pasa un objeto de esta como parámetro. Por ejemplo:

De esta forma podemos crear un doble (test double) de UserDAO para que se comporte como queramos, y pasárselo a ServiceUser.

Si no inyectas dependencias en tus clases te vas a dar cuenta que tienes un gran problema con todo el código que has hecho anteriormente ya que no es testeable…
Voy a escribir sobre este tema en el futuro pero si no puedes esperar echa un vistazo al screencast de Sandro Mancuso (@sandromancuso):

http://craftedsw.blogspot.nl/2012/12/screencast-testing-and-refactoring.html

Es oro puro. Explica como convertir código no testable en testable y añadirle test sin romper nada.

Conclusiones

Aunque tiene sus dificultades el escribir test unitarios, es muy fácil empezar y los beneficios son muchos.

Links

Modificado 08-01-2017: Cambio formateador de código a GitHub