Mostrando entradas con la etiqueta Test unitarios. Mostrar todas las entradas
Mostrando entradas con la etiqueta Test unitarios. Mostrar todas las entradas

miércoles, 25 de mayo de 2016

Pequeño ejemplo en Python y TDD

Imagen de http://code.tutsplus.com/tutorials/beginning-test-driven-development-in-python--net-30137


Hace un tiempo leí el libro Test-Driven Development by Example de Ken Beck y me pareció un gran libro. Es una gran introducción a TDD.

Una parte del libro no entendí bien. La relacionada con un ejemplo de xUnit.
En esa parte del libro, implementa una librería para poder testear código y el ejemplo que va desarrollando está escrito en Python y no conocía el lenguaje. Creo que el lenguaje fuera Python hizo que no lo entendiera.

Así que para poder entenderlo y aprender un poco Python he escrito el código resultado de cada capítulo en un fichero mientras iba leyendo el libro. He subido los ficheros a:

https://github.com/kikers25/tddByExamplePython

Se puede comparar un fichero (con Winmerge) con otro para ver cual han sido los cambios en cada capítulo. Por ejemplo, los cambios entre el capítulos 18 y 19 serían:

Python

Espero que te resulte útil.


domingo, 16 de agosto de 2015

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:





martes, 14 de julio de 2015

Refactorizar y test unitarios


Imagen de http://blog.arnaud-lefebvre.fr/

El proceso que sigo cuando estoy desarrollando una nueva funcionalidad es:
  1. desarrollo la nueva funcionalidad de la forma más rápida posible para que simplemente funcione.
  2. refactorizo el código para que sea más fácil de entender y para que sea más fácil de mantener.
Refactorizar es cambiar el código sin cambiar su comportamiento. El código debe de funcionar de la misma manera antes y después de cambiarlo.
Refactorizar consiste en muchos cambios pequeños en el código que hacen que nuestro código y diseño mejoren.

Cada vez que cambiamos nuestro código hay que comprobar que funciona. Los test unitarios son la forma más rápida que conozco de comprobar que el código funciona.
Si no tengo test unitarios tengo que ejecutar la aplicación y comprobar manualmente que el código que he cambiado funciona. El refactorizar se hace muchas veces por lo que hacer esta comprobación consumirá mucho de nuestro tiempo.

Resumen

Refactorizar es muy importante en mi proceso de desarrollo porque hace que mi código sea fácil de entender y de mantener.
Escribo test unitarios ya que es la forma más rápida que conozco de comprobar que mi código sigue funcionando después de refactorizar.
Si refactorizar es también importante para ti, test unitarios también deberían de ser importante debido a que la otra opción, que es comprobar de forma manual, te va a tomar mucho mas tiempo.

martes, 3 de marzo de 2015

Dobles de test con jMock en un ejemplo

Introducción

En los dos últimos artículos (este y este) hemos visto cómo crear nuestros propios dobles de test. En este artículo vamos a ver un ejemplo de una librería de dobles de test: jMock.

jMock

Hay muchas librerías de dobles de test, como easyMock, Mockito y JMockit. La mayoría de esta librerías están bien y te van a ayudar a crear dobles de test que te faciliten el trabajo de testear una clase.

Yo utilizo jMock porque me ayuda a mejorar el diseño de mi código. Por defecto, nos obliga a tener que declarar de forma explícita cuales son las llamadas que todos los dobles de test que utilicemos en nuestro código a probar.
Si, por ejemplo, en un método tenemos un doble de test que llamamos a tres de sus métodos, tenemos que especificar que permitimos la ejecución de los tres métodos.

Al tener que añadir todas las llamadas al principio del test, podemos ver si hay algo que huele mal (smell) en nuestro código, y así poder mejorar nuestro diseño.

Un inconveniente de jMock comparado con otras librerías como easyMock es que es más complicado de leer el código, pero personalmente es más que suficientemente legible el código.

Ejemplo

En el artículo sobre dobles de test de tipo spies, hay un ejemplo con dos tipos de dobles: spies y stub. Vamos a utilizar ese mismo ejemplo, junto con el mismo código de producción pero añadiendo un test utilizando jMock. Al tener un test con dobles de test hecho por nosotros y otro test con dobles de test de jMock nos va a permitir el ver la diferencia de utilizar la librería y nuestro código.

Vamos a utilizar el mismo código de producción y añadiremos al código de test un nuevo test utilizando jMock.



Creo que el código de producción que aparece arriba es fácil de entender, pero si necesitas alguna explicación extra, en el artículo sobre spies puedes encontrarla.
A continuación, el código de test.



Ambos test hacen lo mismo, comprueban que UserService.findOrCreateWith llama al método sendEmailTo de EmailService, o lo que es lo mismo, comprueban que se envía un email.
La mayor diferencia entre ambos test es que el primero falla cuando nuestro doble de test devuelve false y que el segundo falla lanzando una excepción.

El test con nuestro código de dobles de test comprueba de forma explícita que es llamado el método. Y el test con los dobles de test con jMock comprueba de forma implícita que es llamado el método.

El test should_send_an_email_when_a_user_was_created_with_jmock, dentro de Expectations comprueba que es llamado un vez el método EmailService.sendEmailTo en:



Nuestro doble de test también podría lanzar una excepción cuando no es llamado el método, pero para ello tendríamos que crear un método que realizara la comprobación y lanzara la excepción. Este método debería de utilizarse al final del test.
jMock no necesita que llamemos a ningún método para comprobar las llamadas a los colaboradores porque siempre comprueba internamente (gracias a la anotación Rule) al final de cada test si las expectativas de cada doble de test fueron satisfechas.

Dobles de test propios o librería

Cuando descubrí los dobles de test, los creaba todos a mano, pero ahorré mucho tiempo cuando empecé a utilizar librerías.
Actualmente la mayoría de mis dobles de test los creo con jMock.

Aunque utilizo mas jMock que mis propios dobles de test, siempre hay casos en los que resulta mucho más fácil el crear mis propios dobles de test.
Por ejemplo, cuando resulta complicado el añadir un doble de test con jMock siempre utilizo mis propios dobles de test.
Otra ejemplo sería, cuando no voy a utilizar para nada en los test un colaborador creo un doble de test de tipo dummy o stub para que sea más fácil de entender el test.

martes, 24 de febrero de 2015

Un ejemplo de doble de test con spies

Imagen de https://www.organicconsumers.org

Introducción


En el artículo anterior hablé sobre los dobles de test y los dobles de test de tipo stubs. Los stubs son los tipos de dobles de test que más utilizo en mi día a día.
En este artículo voy a hablar sobre otro tipo de dobles de test: spies.

Tipos de test unitarios

Una forma de clasificar los test unitarios es a partir de la forma que comprueban que nuestro código de producción funciona. Así, tenemos test que comprueban el estado y test que comprueban el comportamiento.
  • Estado: Estos test primero realizan operaciones y comprueban que el resultado es el esperado. La comprobación de que el resultado es el esperado se hace a través de los métodos de tipo assert, como por ejemplo assertEquals, assertTrue y assertNull. Como puedes imaginarte, este tipo de test unitario es el más común y un porcentaje muy alto de los test son de este tipo.
  • Comportamiento: Estos test lo que hacen es comprobar que se llaman a ciertos métodos de los objetos colabores de la clase que estamos testeando. Lo que comprueban es la interacción de nuestro método con los colaboradores. Los dobles de test de tipo spies nos ayudan a realizar esta comprobación, es decir, nos ayudan a testear el comportamiento de nuestro código de producción.

Spies: un ejemplo

Los tipos de test spies son objetos que graban información de como han sido llamados.
Vamos a verlo con un ejemplo. Primero, vas a ver el código de producción y luego el código de test.

Tenemos un método findOrCreateWith de la clase UserService, que devuelve un usuario a partir de su email.
Si el usuario con ese email existe en la base de datos, lo devuelve. Pero si no existe, lo añade a la base de datos y posteriormente envía un email a esa dirección.
Entonces, si existe un usuario con ese email lo devuelve, pero si no existe lo crea y envía un email.



Algunos comentarios sobre el código de arriba:
  • UserService tiene dos objetos colaboradores: UserDAOEmailService.
  • UserDAO se encarga de manejar el usuario en la base de datos.
  • EmailService se encarga de enviar emails.
  • UserDAO y EmailService son interfaces.
  • El método UserDAO.findUserBy siempre devuelve un objeto de tipo User. El método isEmpty de User devuelve true cuando es un objeto vacío, ya que no está en la base de datos, y false cuando es el objeto real. No me gusta devolver null en ningún método por lo que utilizo este patrón. Si quieres saber más escribí un artículo sobre esto aquí.
Después de ver el código de producción, lo que queremos es comprobar que cuando el usuario no existe en la base de datos enviamos un email al usuario, o lo que es lo mismo llamamos al método sendEmailTo del colaborador EmailService.



En el test should_send_an_email_when_a_user_was_created los pasos que he seguido y que separado con un línea en blanco son:

  1. Inyectar un stub de tipo UserDAO y un spy de tipo EmailService a UserService
  2. Llamar al método que estamos testeando: findOrCreateWith
  3. Finalmente, comprobar que se ha enviado el email. Esto se hace comprobando que el método wasSentEmail vale true. Y la única manera de que wasSentEmail sea true es que sendEmailTo halla sido llamado.


Mejoras

No estoy del todo contento con el código de producción que he escrito, así que si alguien me puede ayudar a mejorarlo estaré más que contento en leerlo. Los siguientes puntos describen algunas de las partes que son mejorable bajo mi punto de vista:

  • findOrCreateWith es un nombre horrible para llamar un método. Quizás hubiera sido mejor el llamarlo find o create.
  • EmailService tampoco me gusta como nombre. Quizás SenderEmail hubiera sido mejor.
  • Es importante que en un mismo método halla el mismo nivel de abstracción o lo que es lo mismo, que se utilice el mismo lenguaje. Creo que en este caso findOrCreateWith no está en el mismo nivel. Una pista que me hace pensar eso es que la clase que estamos testeando se llama UserService e inyectamos un objeto que se llama de forma parecida: EmailService.
El código de test no es tampoco perfecto, pero una de las razones es porque lo que he intentado simplificar al máximo.

Fuentes

  • http://googletesting.blogspot.nl/2013/03/testing-on-toilet-testing-state-vs.html
  • http://www.martinfowler.com/bliki/TestDouble.html



lunes, 16 de febrero de 2015

Un ejemplo de doble de test con stubs


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.


lunes, 9 de febrero de 2015

5 consejos para escribir mejores test unitarios

Cuando empecé a escribir test unitarios cometí muchos errores y seguro que todavía cometo y cometeré muchos otros. En este artículo quiero ayudar a la gente que empieza a que no cometan los mismos o al menos a que puedan identificarlos.

1. Una razón para fallar

Cuando un test falla tiene que estar muy claro cual es la razón por la que ha fallado y si nuestro test unitario esta comprobando mas de una cosa, tendremos que mirar todas esas partes del código y eso nos va a costar mas tiempo de lo necesario.
Por el contrario, si solo comprueba una parte específica de nuestro código de producción, va a resultar trivial el solucionarlo cuando el test falle.
Como regla general, tener solo un assert, es la mejor forma de cumplir esta buena práctica.

2. Nombre del test

El nombre del test es muy importante, ya que al ejecutarlo lo primero que vas a ver cuando falle es el nombre. Así que, el nombre del test (junto con el mensaje de error) debe de decirte el porqué ha fallado sin tener que ver el código de producción.
El nombre tiene que contener el qué se está testeando y cual es el resultado que se espera. Como puedes imaginarte, al contener esta información el nombre del test no va a ser corto. Por lo que se podría decir que, al contrario que con el código de producción, el nombre va a ser largo.
En mi caso, utilizo el siguiente formato:
should_elResultadoEsperado_when_elCasoQueEstoyProbando

Un ejemplo del formato que utilizo seria el siguiente:
should_throw_exception_when_there_is_no_internet_connection

Hay mucha gente que en vez de utilizar un guión bajo para separar las palabras pone en mayúsculas la primera letra de la palabra:
shouldThrowExceptionWhenThereIsNoInternetConnection

No hay ninguna opción que sea la mejor. Utilizo guiones bajos porque personalmente entiendo mejor el texto.


3. Rápido

Que tus test unitarios se ejecuten en pocos milisegundos es una de las cualidades más importantes que deben tener.
Y te preguntarás el porqué es tan importante que tus test unitarios sean rápidos. Principalmente, porque podrás ejecutarlos cada pocos minutos y eso te permitirá el conocer si hay algo que no funciona como esperas, casi al momento de escribirlo.
El principal problema de velocidad en los test es debido a que el código habla con la base de datos, con el sistema de archivos o que acceden a internet.
Para solucionar este problema puedes utilizar dobles de test, en vez de la clase que realiza este tipo de operaciones que tardan tanto en ejecutarse.
Escribir dobles de test es fácil pero en general se suelen utilizar librerías que hacen el trabajo por ti y te dan más opciones. En Java tienes muchas. En mi caso, utilizo JMock.

4. Fixture

Ten mucho cuidado al compartir variables y métodos entre test. Más de una vez no entendía porque un test fallaba o porque un test no fallaba y la razón era porque tenía compartía variables entre varios test unitarios.
La mejor forma de evitar esto es no compartir variables entre test. También se pueden compartir variables, pero para evitar estos problemas, inicializa las variables compartidas en el método setUp de la clase.

5. Test que no comprueba nada

Cuando veas el código de tu test tiene que estar más que claro cual es la manera de que falle el test. Por eso, si tienes un test sin ningún assert y sin manejar las excepción, ¿como vas a saber cuál es la razón de que ha fallado?
Creo que todo test tiene que tener de forma explícita cual es su razón de fallar.
Así, un test que no comprueba el resultado, que no verifica que un objeto colaborador se ha llamado o que no maneja las excepciones no tiene mucho significado.


lunes, 26 de enero de 2015

Cómo aprendí Test Driven Development





TL; TR

Busca a alguien con experiencia en TDD, empieza aprendiendo a escribir buenos test unitario, haz katas para aprender lo básico de TDD y después lánzate a hacer TDD en partes fáciles de implementar de tu aplicación.

Objetivo: como comenzar

El otro día (bueno, no fue el otro día, fue en octubre...) me preguntaron como he aprendido test driven development (TDD) y quería extenderme en la explicación y compartirlo por si a alguien le resulta de utilidad.

Como contexto, decir, que llevo un año practicando TDD y creo que tengo suficientemente fresco los primeros pasos que dí como para escribir este artículo.

Después de leer muchos blogs de como comenzar y de darle a la cabeza, me planteé aprender TDD en dos fases. Primero, aprender como escribir test unitarios (ver mi artículo anterior) y posteriormente TDD.

Lo hice de esta manera porque pensé (y todavía pienso) que TDD está muy vinculado con escribir test de forma correcta y sin una buena base debe de resultar más difícil el aprenderlo.

Primera fase: escribir test unitarios

Así que empecé a escribir test unitarios en casa después de haber aprendido lo básico sobre JUnit, la librería que se suele utilizar como estándar para escribir test unitarios.

Es muy importante que los test que escribas estén bien implementados, ya que solo hay algo peor que un código sin test unitarios, código con test con falso verde. Es decir, que no fallan pero porque no pueden fallar o porque hacen otra cosa diferente que lo que aparece en su nombre.

Después de unos meses escribiendo test me leí el libro Effective Unit Testing de Lasse Koskela y me ayudó radicalmente a mejorar mis test debido a que mientras me lo estaba leyendo recordaba los test que había escrito y como podía haberlo solucionado mejor gracias a los consejos del libro. Me ayudó principalmente en mejorar la legibilidad y la mantenibilidad de mis test.

Luego, empecé en el trabajo a escribir test y me resultó muy pero que muy complicado en muchos casos el escribir test.
Y en un punto me dí cuenta que el problema era que el código que había escrito no era testeable, ya que en un porcentaje muy alto, el código que no tiene test no era testeable. Como comenta Michael Feathers:

Código que no ha sido diseñado para ser testeable no es testeable. La testeabilidad no es cualidad que aparece accidentalmente.

Fue un palo grande pero, después de asumirlo, empecé con la tarea de escribir nuevo código diseñado para ser testeable y poco a poco empecé a hacerlo mejor.


Segunda fase: Test driven development

Al terminar de leer el libro Test driven de Lasse koskela (este tío es un gran escritor), decidí que ya era el momento de empezar con TDD y empecé a hacer katas / code katas.

Según la wikipedia, una kata es un ejercicio que ayuda a los desarrolladores a mejorar sus habilidades como programador.

Puedes encontrar muchos tipos de katas y de muchos niveles en Internet. En cuanto a las herramientas puedes utilizar tu IDE favorito junto con la versión que conozcas de Java y de Junit o puedes utilizar cyber-dojo

Cyber-Dojo es una aplicación web que permite escribir código en tu navegador, compilarlo y ejecutarlo. Además no es solo para Java, sino que permite muchísimos lenguajes como C, C++, C#, Clojure, Grovy y un largo etcétera.

Hice bastantes katas pero no muchas porque me resultaba aburrido el hacerlo solo.

El paso a practicar TDD en el trabajo fue lento. Básicamente, en las nuevas funcionalidades que desarrollaba le dedicaba partes de mi tiempo a practicar TDD. Poco a poco el tiempo a TDD fue creciendo hasta dedicarle todo el tiempo.

Recomendaciones 


  • Encuentra tu motivación. TDD es difícil de aprender así que tener una motivación te ayudará a seguir. Mi motivación consistía en reducir el número de errores en mi código. Siempre he dedicado un tiempo importante en mi trabajo a solucionar errores y estaba cansado que aunque ponía mucho empeño en solucionarlo de la forma correcta los errores que encontraba y en desarrollar nuevo código sin errores, siempre salían muchos.
  • Busca un mentor o alguien con experiencia en TDD. Te va a ahorrar más de un quebradero de cabeza y mucho tiempo. He aprendido TDD por mi cuenta porque nadie en mi entorno tenía experiencia y con diferencia lo que más eché de menos fue el no haber tenido a nadie a quien poder preguntar. Ahora pensando, a lo mejor hubiera podido haber utilizado Internet para buscar a alguien.
  • Haz cursos que te ayuden en avanzar. En mi caso, hice el curso de Carlos Blé sobre Test dobles y me ayudó a dar el salto a usar dobles de test de forma más activa en mi código.


Nota. Después de leer el artículo varias veces creo que he resumido mucho los pasos que he seguido. Si echas en falta alguna parte, solo tienes que preguntar :-)

domingo, 14 de diciembre de 2014

Código Java testeable

Introducción

Hace un tiempo, después de leer en casa sobre test unitarios y de haber hecho bastantes ejercicios, decidí escribir test unitarios en el trabajo.

La idea era escribir los test unitarios después de escribir el código de producción.
Nota: En varios libros y artículos comentan que la mejor forma de comenzar a escribir test unitarios en el trabajo es cuando surjan incidencias o problemas y creo que tienen razón. Si lo hubiera hecho así hubiera sido más fácil :-)

Tras varios intentos fallidos me encontraba realmente frustrado: ¿porqué me resultaba tan complicado escribir test unitarios si en mi casa no tenía ningún problema?

Entonces llegué a un artículo de Michael Feathers donde aparece la siguiente frase en el primer párrafo:

Code that wasn’t designed to be testable is not testable. Testability is not a quality that appears accidently. Or does it?

Código que no ha sido diseñado para ser testeable no es testeable. La testeabilidad no es cualidad que aparece accidentalmente. ¿O si?


Entonces, me dí cuenta que me resultaba tan complicado escribir test unitarios porque mi código no era testeable.
Este hallazgo fue a la vez una liberación y un palo porque a quién le gusta saber que su código no está diseñado de forma adecuada.

Código Java testeable

Si os habéis leído el artículo, poco más os puedo contar. Bueno, puedo contaros mi versión del artículo porque personalmente me resulta complicado el acordarme de todos los sitios en los no tengo que que estar pendiente donde pongo código TUF (Test Unfriendly Feature).

Así, para escribir código testeable hay que seguir los siguiente consejos:

  1. No escribas métodos estáticos o bloques de código estático.
  2. No escribas clases finales ni métodos finales
  3. Inyecta en tu código los colaboradores de la clase a testear (principalmente las que "hablan" con la base de datos, internet, sistema de ficheros o interfaz de usuario) y añádelas como atributos. Escribí un artículo sobre la inyección de dependencias en http://www.enrique-martin.com/2014/05/injeccion-de-dependencias-en-java-para.html
  4. Inyecta interfaces en lugar de clases. Es mejor depender de abstracciones que de implementaciones. Además creo que tiene sentido porque ¿para qué queremos inyectar una clase concreta si lo que queremos es que nuestro código sea más flexible?

Si practicas los dos primeros puntos tu código va a mejorar mucho en testeabilidad. En cuanto a los puntos 3 y 4 difieren a lo que aparece en el artículo.

Creo que si sigues los puntos 3 y 4 la mayoría de los problemas que se comentan en el artículos, como son en la clases privadas y en los constructores, se solucionan.

Porqué

Como pone en el artículo, todos los consejos están relacionados con la forma en que se escriben test unitarios.
Un test unitario debe ser rápido y repetible y suelen no cumplirse estas propiedades si, por ejemplo, se accede a base de datos en el código.
Así, lo que se hace es crear una subclase de la clase donde está el problema y se sobrescribe el método para que se comporte como queramos. Si no se puede sobreescribir el método resulta muy complicado el testearlo.

Por ejemplo, si queremos que un método devuelva siempre una lista vacía, y este método es estático. No podremos sobreescribirlo en la subclase, y por lo tanto no podemos hacer que se comporte como queremos.



En conclusión, si tu código no tiene test unitarios seguramente tú código no es testeable por lo que te va a resultar complicado escribir test unitarios.

jueves, 4 de septiembre de 2014

Por qué escribir test unitarios

El otro día leí un artículo de Gil Zilberfeld sobre la economía de los test unitarios.
En la siguiente dirección podéis encontrar un enlace a su blog:

http://www.gilzilberfeld.com/2014/09/the-economics-of-unit-testing.html

Básicamente lo que quiere decir el artículo es que:

Escribir test unitarios te permite ganar dinero

Así de simple.

¿Y cómo lo hacen? De la siguiente manera:
  • Escribir test unitarios hace que el tiempo en liberar cada versión se reduzca
  • Si el tiempo en cada versión se reduce, puedes vender tu producto y las nuevas funcionalidades antes. 
  • Además el números de errores (bugs) se reduce por lo que podemos dedicar más tiempo a nuevas funcionalidades.
Pero, ¿cómo puede reducir el tiempo en liberar cada versión? Muy fácil, porque aunque el tiempo dedicado al desarrollo aumenta, el tiempo dedicado a pruebas (testing) se reduce drasticamente.

Tengo que decir que, en mi experiencia, escribiendo test unitario he reducido mucho el tiempo dedico a probar a pruebas. Además, también he comprado que el número de errores se han reduce considerablemente. Por lo que ambas afirmaciones son ciertas.

Claro que hay que dedicar tiempo al principio a aprender como escribir test unitario pero en cuanto el equipo aprende vamos a conseguir los beneficios que he comentado anteriormente.

En resumen y como dice el autor del blog:

Unit testing shortens the current and future release cycles at the expense of initial extra development work.
Los test unitarios reducen el actual y futuros ciclos de liberación de versiones, a costa de un trabajo extra inicial de desarrollo.

El por qué de este artículo

Como te debes haber dado cuenta, me he dedicado hasta aquí a resumir el artículo de Gil Zilberfeld, pero la idea de este artículo es otra.
Creo que si queremos "vender" a nuestros compañeros, jefes o amigos el por qué debemos utilizar test unitarios esta es la mejor forma de hacerlo.
Primero, porque estamos utilizando un lenguaje que todo el mundo entiendo. 
Y segundo, porque al fin y al cabo nosotros estamos trabajando por dinero y el dinero es un lenguaje que todo el mundo entiende.

Por otro lado, si sólo quieres empezar a escribir test unitarios no necesitas pedir permiso a nadie. Puedes empezar hoy mismo a hacerlo. A la larga verás como fue una buena decisión.

* Modificado 12-09-09: Corregidos algunos errores gramaticales

miércoles, 14 de mayo de 2014

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 UserService utilizando clase UserDAO
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 UserService utilizando clase FakeUserDAO
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. Constructor: como su nombre indica añadimos las dependencias en el constructor
  • 2. Método o Setter: Se puede crear un método por cada dependencia o uno para todas.
  • 3. 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.
  • 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.

viernes, 2 de mayo de 2014

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:


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

sábado, 1 de marzo de 2014

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í empezé 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 mayoria 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 inyestas 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):


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