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 :-)

miércoles, 14 de enero de 2015

Objetivos 2015

Pues ha pasado un año, ¡un año! Que se dice pronto.
La primera impresión que tengo al echar la vista atrás es que ha sido un año duro, duro e intenso pero en el que he aprendido mucho.

Primero quiero ver cuantos de mis objetivos del año pasado he cumplido y posteriormente el escribir una nueva lista con los nuevos objetivos.

En la siguiente lista aparece en color negro mis objetivos del año pasado y en rojo las conclusiones de si lo he conseguido cumplir o no:
  • Escribir un blog de forma regular: Para empezar con buen pie aquí tengo mi primer artículo que espero que sea el primero de muchos. Con un artículo cada dos semanas es más que suficiente. Empezamos mal. Este primer objetivo no he estado ni cerca. Escribir un artículo cada 2 semanas significa 24 artículos anuales y este año he escrito 8.
  • Aprender más lenguajes de programación: En la página code 2013 están realizando una gráfica con todos los lenguajes con los que está programando la gente. Al participar me he dado cuenta que no he aprendido nuevos lenguajes este año y que hay que remediarlo lo más pronto posible. Este tampoco. No he aprendido ningún lenguaje de programación nuevo y ni siquiera he hecho un amago de aprenderlo.
  • Utilizar de forma regular TDD: He estado aprendiendo mucho sobre TDD pero solo lo pongo en práctica de forma puntual y creo que ha llegado el momento de utilizarlo en mi día a día. Puedo decir que esta la he cumplido porque utilizo de forma regular TDD (test driven development), aunque no diariamente. Estoy muy contento de haber conseguido este objetivo porque ha sido duro. Si vas a aprender TDD es mejor tener a alguien que tenga experiencia.
  • Sentirme cómodo al hablar en inglés: Aunque hablo, escribo y leo en inglés de forma regular cada vez que hablo en inglés no me siento nada cómodo y hay veces que me pongo nervioso. Creo que es un problema de mi actitud y no del nivel que tengo. El cambio de empresa y el tener que comunicarme de forma diaria con un grupo mas o menos grande de gente me ha ayudado mucho a conseguir este objetivo. Así que lo voy a dar por conseguido.
  • Aprender Holandés: Al menos lo básico. Llevo viviendo aquí casi dos años y no entiendo casi nada cuando la gente habla. Me apunté a clases, que consistían en 2 días a las semana y 3 horas y cuarto cada día. Pero... no fui ni a la primera clase. Creo que no era el momento para aprender un nuevo idioma. Que no estaba preparado para aprender dos lenguas a la vez.
  • Hacer ejercicio: Cuando no hago ningún tipo de ejercicio lo noto y no solo físicamente. La bicicleta ayuda pero sin realizar algún otro deporte no es suficiente. Quiero correr un día a la semana e ir al gimnasio dos veces a la semana. No voy dos veces a la semana al gimnasio pero si que voy una vez. Me encuentro mejor no solo por el ejercicio, sino también por cuidar mejor lo que como. Podía ser mejor pero está bien. 

La nueva lista de objetivos, que aparece abajo, no va a variar mucho de la del año pasado:
  • Escribir en el blog de forma regular: dos artículos a la semana. Ahora sé que me voy a tener que esforzarme mucho para conseguir este objetivo pero seguro que merece la pena el lograrlo.
  • Aprender más lenguajes de programación: voy a intentar Groovy y/o Scala. Todo el l tiempo que le he dedicado a TDD lo puedo dedicar a aprender nuevos lenguajes.
  • Aprender Holandés: he aprendido la lección. Voy a intentar coger un curso que no me ocupe mas de un par de horas a la semana.
  • Hacer ejercicio dos veces a la semana: mi vida ha mejorado haciendo ejercicio así que hacer mas ejercicio seguro que lo mejora más.
  • Mejorar mi pronunciación al hablar en inglés: tengo los típicos problemas de pronunciación de los españoles y alguno propio. Creo que mejorándolos me ayudaran a comunicarme mejor.
  • Aplicar agile prácticas en mi día a dia. He leído bastantes libros sobre metodologías ágiles y creo que ya ha llegado el momento de aplicar lo aprendido.
En resumen, de los objetivos he conseguido cumplir dos. No son muchos pero estoy contento, podía haber sido peor :-)

Este próximo año espero poder aprender al menos la mitad de lo que he aprendido en 2014.

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.