24 JanÉrase una vez… el diseño ágil con TDD

Después de varias semanas de retiro en las lejanas tierras de Huelva, obligado por razones familiares, y después del fenomenal éxito del libro “Diseño Ágil con TDD” que mi buen amigo Carlos Blé me ha dejado prologar, debo reconocer que ahora mismo no tengo mucho que aportar en este blog salvo extraer ese prólogo. Me siento bastante orgulloso de él, no sólo porque es original, sino porque creo que resume bastante bien cómo enfocar un desarrollo de software guiado por las pruebas además de reflejar el espíritu del cambio (aunque tardío) que se está produciendo en nuestro sector y que desde iniciativas como Agile Spain o agilismo.es trato de apoyar en primera persona. Espero que os guste:

Érase una vez que se era, un lejano país donde vivían dos cerditos, Pablo y Adrián, que además eran hermanos. Ambos eran los cerditos más listos de la granja y por eso el gallo Iván (el gerente de la misma) organizó una reunión en el establo, donde les encargó desarrollar un programa de ordenador para controlar el almacén de piensos. Les explicó que quería saber en todo momento cuántos sacos de grano había y quién metía y sacaba sacos de grano del almacén. Para ello sólo tenían un mes, pero les advirtió de que en una semana quería ya ver algo funcionando. Al final de esa primera semana, eliminaría a uno de los dos.

Adrián, que era el más joven e impulsivo, inmediatamente se puso manos a la obra. “¡No hay tiempo que perder!”, decía. Y empezó rápidamente a escribir lineas y lineas de código. Algunas eran de un reciente programa que había ayudado a escribir para la guardería de la vaca Paca. Adrián pensó que no eran muy diferentes un almacén de grano y una guardería. En el primero se guardan sacos y en el segundo pequeños animalitos. De acuerdo, tenía que retocar algunas cosillas para que aquello le sirviera, pero bueno, esto del software va de reutilizar lo que ya funciona, ¿no?

Pablo, sin embargo, antes de escribir una sola línea de código comenzó acordando con Iván dos cosas: qué era exactamente lo que podría ver dentro de una semana y cómo sabrían que efectivamente estaba terminada cada cosa. Iván quería poder conocer cuanto antes cuántos sacos de grano había en cada parte del almacén porque sospechaba que en algunas partes del almacén se estaban acumulando sacos sin control y se estaban estropeando. Como constantemente tenían que entrar y salir sacos del almacén, no podía saber cuántos había ahora mismo, así que acordaron ir contabilizando cuántos había en cada zona del almacén y que cada vez que entrara o saliera un saco apuntarían a qué zona iba o de qué zona venía. Así, en poco tiempo podrían tener una idea clara del uso que se estaba dando a las distintas zonas del almacén.

Mientras Adrián adelantaba a Pablo escribiendo muchas líneas de código, Pablo escribía primero las pruebas automatizadas. A Adrián eso le parecía una pérdida de tiempo. ¡Sólo tenían una semana para convencer a Iván!

Al final de la primera semana, la demo de Adrián fue espectacular, tenía un control de usuarios muy completo, hizo la demostración desde un móvil y enseñó además las posibilidades de un generador de informes muy potente que había desarrollado para otra granja anteriormente. Durante la demostración hubo dos o tres problemillas y tuvo que arrancar de nuevo el programa, pero salvo eso, todo fue genial. La demostración de Pablo fue mucho más modesta, pero cumplió con las expectativas de Iván y el programa no falló en ningún momento. Claro, todo lo que enseñó lo había probado muchísimas veces antes de hacer la demostración gracias a que había automatizado las pruebas. Pablo hacía TDD, es decir, nunca escribía una linea de código sin antes tener una prueba que le indicara un error. Adrián no podía creer que Pablo hubiera gastado más de la mitad de su tiempo en aquellas pruebas que no hacían más que retrasarle a la hora de escribir las funcionalidades que había pedido Iván. El programa de Adrián tenía muchos botones y muchísimas opciones, probablemente muchas más de las que jamás serían necesarias para lo que había pedido Iván, pero tenía un aspecto “muy profesional”.

Iván no supo qué hacer. La propuesta de Pablo era muy robusta y hacía justo lo que habían acordado. La propuesta de Adrián tenía cosillas que pulir, pero era muy prometedora. ¡Había hecho la demostración desde un móvil! Así que les propuso el siguiente trato: “Os pagaré un 50% más de lo que inicialmente habíamos presupuestado, pero sólo a aquel de los dos que me haga el mejor proyecto. Al otro no le daré nada.”. Era una oferta complicada porque por un lado, el que ganaba se llevaba mucho más de lo previsto. Muy tentador. Por el otro lado, corrían el riesgo de trabajar durante un mes completamente gratis. Mmmmm.

Adrián, tan impulsivo y arrogante como siempre, no dudó ni un instante. “¡Trato hecho!”, dijo. Pablo explicó que aceptaría sólo si Iván se comprometía a colaborar como lo había hecho durante la primera semana. A Iván le pareció razonable y les convocó a ambos para que le enseñaran el resultado final en tres semanas.

Adrián se marchó pitando y llamó a su primo Sixto, que sabía mucho y le aseguraría la victoria, aunque tuviera que darle parte de las ganancias. Ambos se pusieron rápidamente manos a la obra. Mientras Adrián arreglaba los defectillos encontrados durante la demo, Sixto se encargó de diseñar una arquitectura que permitiera enviar mensajes desde el móvil hasta un webservice que permitía encolar cualquier operación para ser procesada en paralelo por varios servidores y así garantizar que el sistema estaría en disposición de dar servicio 24 horas al día los 7 días de la semana.

Mientras tanto, Pablo se reunió con Iván y Bernardo (el encargado del almacén) para ver cuáles deberían ser las siguientes funcionalidades a desarrollar. Les pidió que le explicaran, para cada petición, qué beneficio obtenía la granja con cada nueva funcionalidad. Y así, poco a poco, fueron elaborando una lista de funcionalidades priorizadas y resumidas en una serie de tarjetas. A continuación Pablo fue, tarjeta a tarjeta, discutiendo con Iván y Bernardo cuánto tiempo podría tardar en terminarlas. De paso aprovechó para anotar algunos criterios que luego les servirían para considerar que esa funcionalidad estaría completamente terminada y eliminar alguna ambigüedad que fuera surgiendo. Cuando Pablo pensó que, por su experiencia, no podría hacer más trabajo que el que ya habían discutido, dió por concluida la reunión y se dispuso a trabajar. Antes que nada resolvió un par de defectos que habían surgido durante la demostración y le pidió a Iván que lo validara. A continuación se marchó a casa a descansar. Al día siguiente, cogió la primera de las tarjetas y, como ya había hecho durante la semana anterior, comenzó a automatizar los criterios de aceptación acordados con Iván y Bernardo. Y luego, fue escribiendo la parte del programa que hacía que se cumplieran esos criterios de aceptación. Pablo le había pedido ayuda a su amigo Hudson, un coyote vegetariano que había venido desde América a pasar el invierno. Hudson no sabía programar, pero era muy rápido haciendo cosas sencillas. Pablo le encargó que comprobara constantemente los criterios de aceptación que él había automatizado. Así, cada vez que Pablo hacía algún cambio en su programa, avisaba a Hudson y éste hacía, una tras otra, todas las pruebas de aceptación que Pablo iba escribiendo. Y cada vez había más. ¡Este Hudson era realmente veloz e incansable!

A medida que iba pasando el tiempo, Adrián y Sixto tenían cada vez más problemas. Le terminaron echando la culpa a todo el mundo. A Iván porque no les había explicado detalles importantísimos para el éxito del proyecto. A la vaca Paca porque había incluido una serie de cambios en el programa de la guardería que hacía que no pudieran reutilizar casi nada. A los inventores de los SMS y los webservices porque no tenían ni idea de cómo funciona una granja. Eran tantos los frentes que tenían abiertos que tuvieron que prescindir del envío de SMS y buscaron un generador de páginas web que les permitiera dibujar el flujo de navegación en un gráfico y a partir de ahí generar el esqueleto de la aplicación. ¡Eso seguro que les ahorraría mucho tiempo! Al poco tiempo, Sixto, harto de ver que Adrián no valoraba sus aportaciones y que ya no se iban a usar sus ideas para enviar y recibir los SMS, decidió que se marchaba, aun renunciando a su parte de los beneficios. Total, él ya no creía que fueran a ser capaces de ganar la competición.

Mientras tanto, Pablo le pidió un par de veces a Iván y a Bernardo que le validaran si lo que llevaba hecho hasta aquel momento era de su agrado. Les hizo un par de demostraciones durante aquellas 3 semanas, lo que sirvió para corregir algunos defectos y cambiar algunas prioridades. Iván y Bernardo estaban francamente contentos con el trabajo de Pablo. Sin embargo, entre ellos comentaron más de una vez: “¿Qué estará haciendo Adrián? ¿Cómo lo llevará?”.

Cuando se acercaba la fecha final para entregar el programa, Adrián se quedó sin dormir un par de noches para así poder entregar su programa. Pero eran tantos los defectos que había ido acumulando, que cada vez que arreglaba una cosa le fallaba otra. De hecho, cuando llegó la hora de la demostración, Adrián sólo pudo enseñar el programa instalado en su portátil (el único sitio donde funcionaba a duras penas) y fue todo un desastre: mensajes de error por todos sitios, comportamientos inesperados… y lo peor de todo: el programa no hacía lo que habían acordado con Iván.

Pablo, sin embargo, no tuvo ningún problema en enseñar lo que llevaba funcionando desde hacía mucho tiempo y tantas veces había probado. Por si acaso, dos días antes de la entrega, Pablo había dejado de introducir nuevas características al programa porque quería centrarse en dar un buen manual de usuario, que Iván había olvidado mencionar en las primeras reuniones porque daba por sentado que se lo entregarían. Claro, Adrián no había tenido tiempo para nada de eso.

Moraleja:

Además de toda una serie de buenas prácticas y un proceso de desarrollo ágil, Pablo hizo algo que Adrián despreció: acordó con Iván (el cliente) y con Bernardo (el usuario) los criterios mediante los cuáles se comprobaría que cada una de las funcionalidades estaría bien acabada. A eso que solemos llamar “criterios de aceptación”, Pablo le añadió la posibilidad de automatizar su ejecución e incorporarlos en un proceso de integración continua (que es lo que representa su amigo Hudson en este cuento). De esta manera, Pablo estaba siempre tranquilo de que no estaba estropeando nada viejo con cada nueva modificación. Al evitar volver a trabajar sobre asuntos ya acabados, Pablo era más eficiente. En el corto plazo, las diferencias entre ambos enfoques no parecen significativas, pero en el medio y largo plazo, es evidente que escribir las pruebas antes de desarrollar la solución es mucho más eficaz y eficiente.

En este libro que ahora tienes entre tus manos, y después de este inusual prólogo, te invito a leer cómo Carlos explica bien clarito cómo guiar el desarrollo de software mediante la técnica de escribir antes las pruebas (más conocido como TDD).

Un cordial saludo,
Jose Manuel Beas

Espero que después de leer esto, los que no hayáis comprado el libro de Carlos sintáis un impulso irrefrenable y lo hagáis rápidamente, y los que ya los hayáis comprado, o al menos leído, dejéis un comentario aquí sobre qué os ha parecido. ¿Cómo mejoraríais la historia? ¿Qué le quitaríais? ¿Le daríais otro enfoque?

Tags: ,

10 JanDiseño emergente

Mientras preparaba una sesión del curso online de Lean Software Development que imparte Alan Shalloway tuve que leer un artículo de él mismo titulado algo así como “Guía para desarrolladores ágiles sobre Lean Software Development” (traducción libre). En este artículo se recopilan detalles interesantes como los principios fundamentales del desarrollo de software “lean” (Nota: demonios, necesito una traducción para este término), pero lo que quizás más me ha gustado ha sido la definición de Diseño Emergente.

Shalloway explica que al abordar un diseño hay básicamente dos opciones: diseñar estríctamente para cubrir los requerimientos (con lo que el código resultante irá siendo progresivamente más difícil de mantener) o diseñar pensando en el futuro (con lo que frecuentemente introducimos más complejidad de lo necesario). Aquí introduce Shalloway una tercera opción que denominamos Diseño Emergente y que resulta de la combinación de tres disciplinas (ojo, dice disciplinas):

  • basar el diseño en patrones para crear arquitecturas resistentes y flexibles a la vez, abaratando así el coste de mantenimiento del código
  • limitar el diseño sólo a los requisitos actuales (no adelantarse a requisitos futuros), consiguiendo así reducir el tamaño y complejidad del código
  • escribir pruebas de aceptación y unitarias automatizadas antes de escribir el propio código, favoreciendo así la construcción de un mejor diseño y haciendo que sea más seguro aplicar cambios

Shalloway también dice que el Diseño Emergente nos ayuda a añadir código nuevo desacoplado del viejo pero sin incorporar complejidades innecesarias. Esto, dicho así, puede resultar un poco difícil de entender, pero en el contexto de desarrollos iterativos tiene mucho sentido.

Trabajar sin un diseño completo desde el principio es algo incómodo por el nivel de incertidumbre que se maneja, pero si lo pensamos fríamente, es muchor mejor postergar las decisiones de diseño mientras tengamos dudas sobre los requisitos. En cualquier caso, me gustaría añadir que trabajar con un Diseño Emergente NO significa trabajar SIN diseño o con un diseño IMPROVISADO. Me remito a las tres disciplinas que enumera Shalloway:

Patrones de diseño

Emplear patrones de diseño contribuye definitivamente a que tengamos un diseño fácil de transimitir (incluso fácil de documentar). (Nota para los arquitectos: es una buena práctica documentar con gran detalle los patrones empleados -aunque no sean del GoF y nos los hayamos “inventado”- y pedir a los desarrolladores que documenten en su código qué piezas de los patrones están implementando.)

Por otro lado, los patrones de diseño nos permiten construir soluciones extensibles puesto que no resuelven nuestro problema sino un problema más general.

Me gustaría añadir que también podemos añadir patrones de análisis (ver libro de Fowler “Analysis Patterns“, p.ej.) a nuestra caja de herramientas con el objetivo de ser capaces de plantear el diseño más adecuado a nuestro problema, pero con la “puerta abierta” a futuras necesidades.

No adelantarse al futuro

Nuestro pequeño gran ego de programador nos empuja indefectiblemente a demostrarle al mundo entero lo listos que somos al ser capaces de ver el futuro y adelantarnos a todos prediciendo las necesidades de los clientes. Para ello solemos diseñar soluciones tan virtuosas como inútiles para el 80% de las necesidades reales de los clientes. Frecuentemente nos encontramos con diseños que transforman datos entre capas una y otra vez hasta llevarlas a la base de datos, validando una y otra vez los datos (antes y después de persistirlos)… aunque esos datos en realidad el cliente nunca los ha necesitado guardar. Alguien decidió en algún momento que era mejor guardarlo todo “por si acaso” y la consecuencia es un código tremendamente difícil de explicar, con muchos defectos de desarrollo y un rendimiento muy mejorable. Muchas veces somos nosotros mismos los que diseñamos así: “por si acaso”.

Pues bien, está demostrado que es mejor no diseñar “por si acaso”. En la mayoría de las ocasiones no estamos haciendo una inversión sino introduciendo complejidad que nunca se verá compensada. En términos económicos eso no es rentable.

Propongo que hagáis el ejercicio de identificar algunas de esas “características avanzadas” que se incorporan “por un pequeño esfuerzo más” y que al final del proyecto nunca se han utilizado. Si además sois capaces de saber cuánto ha sido ese “pequeño esfuerzo más”, quizás lo podáis sumar a los defectos y retrasos provocados por esas “características avanzadas”. Claro, un diseño sin “características avanzadas” es menos “cool”. :-)

Pruebas automatizadas

Muchísimas veces me he encontrado con que teníamos tanto miedo de hacer cambios en el diseño que retorcíamos el diseño existente hasta límites insospechados. En ocasiones, cambiar aquí hacía que allí dejara de funcionar. Lo más terrible era cuando nos enterábamos de ese impacto cuando nos llamaba el cliente para decirnos que les había salido un mensaje muy extraño en la pantalla…. Ay, si hubieramos contado con un conjunto de pruebas automatizadas que cubrieran los requisitos de nuestros clientes…

Hoy día, este problema está claramente superado, si no tenemos un motor de integración continua y desarrollamos nuestras pruebas unitarias y de aceptación es o porque no sabemos o porque no queremos, pero no nos podemos escudar en que no es posible o que es muy costoso.

Instalar Hudson, Continuum o CruiseControl se hace en dos patadas y desarrollar pruebas unitarias con JUnit o TestNG está ampliamente documentado. Si tenéis un frontal web podéis usar Selenium, HttpUnit o algún otro framework similar. Si tenéis web services podéis usar SoapUI o haceros vuestro propio framework “ad hoc” basándolo en HttpUnit, por ejemplo. Y finalmente, para escribir las pruebas de aceptación tenéis desde el viejo Fit (o Fitnesse) hasta Concordion (lógicamente mi recomendación) o incluso easyb o jBehave. Para cada clavo seguro que tenéis vuestro martillo.

Pero Shalloway va un poco más allá y pide escribir las pruebas ANTES que el código. Hay mucho escrito sobre TDD (test-driven development), ATDD (acceptance test-driven development) o BDD (behaviour-driven development), pero os puedo asegurar que practicar el “red-green-refactor” termina dando como resultado diseños y código de mucha mejor calidad, con el beneficio añadido de que tenemos una estupenda red de seguridad formada por las pruebas que hemos ido escribiendo para explicar qué queríamos que hiciera el software y no para explicar lo que ya hacía el software. Además, reducimos el número de defectos y, yo no sé a vosotros, pero a mi no me gusta nada estar resolviendo defectos… a mis jefes tampoco. :-)

Proceso de descubrimiento

Otra afirmación de Shalloway con la que estoy muy de acuerdo es que:

El proceso de desarrollo de software es más un proceso de descubrimiento que de construcción.

Podría dedicar un artículo entero a esta afirmación pero sólo quiero señalar que parte de lo que se va
descubriendo durante este proceso es justamente el Diseño Emergente.

Viendo el desarrollo de software dentro del proceso mayor que lo engloba, el desarrollo del producto, podemos decir (simplificando mucho) que hay 3 fases para desarrollar un producto:

  • descubrir las necesidades del cliente
  • imaginar cómo construir los artefactos que cubrirán esas necesidades
  • construir los artefactos

Shalloway explica muy bien en su artículo cómo empleamos un gran esfuerzo en las dos primeras fases a pesar de que normalmente no somos casi ni conscientes (¿inconscientes?) de ello. Si de repente tuvieramos la oportunidad de reconstruir nuestro sistema tardaríamos digamos un 50%-80% menos del tiempo empleado la primera vez. Ese tiempo “perdido” es el correspondiente a las dos primeras fases: descubrir necesidades y diseñar los artefactos.

El artículo de Shalloway del que he extraído las principales ideas que os he comentado es mucho más rico porque recorre los principios fundamentales de “Lean” y va mostrando qué prácticas ágiles surgen a partir de ellos. Esta muy bien porque así es posible tener un fundamento teórico para unas prácticas y, cuando en ocasiones no es posible aplicarlas, podemos sustituirlas por otras pero respetando los principios, es decir, buscando los mismos beneficios. También compara los procesos de producción JIT (just-in-time) con los procesos ágiles de desarrollo de software, pero esto casi merece otro artículo sólo para él.

Estaría encantado de conocer vuestras opiniones.

27 NovRecursos en castellano

Carlos Ble ha tenido a bien incluirme en una lista de enlaces de recursos sobre TDD, refactoring y otras cosas ágiles. Muchas gracias, Carlos, sobre todo por compartir esa inquietud por las carencias de material en español.

18 NovRefactoring en Español (y 6)

Hoy, por fin, termino esta serie de artículos basados en el ejemplo del primer capítulo del libro de Fowler “Refactoring“.

Recapitulemos un poco antes de comenzar.

Desde la primera entrega hasta ésta no hemos cambiado el comportamiento del sistema: sólo hemos cambiado el diseño interno. A eso le llamamos refactorizar. (Por cierto, mi siguiente entrega de “Aprender inglés es fácil… si sabes cómo” irá justamente sobre esta palabra).

Y estamos seguros de que no ha cambiado el comportamiento porque tenemos una batería de pruebas unitarias que así nos lo han ido demostrando.

A lo largo de las cinco entregas anteriores hemos ido moviendo código dentro de las clases hacia métodos privados que, además, mejoraban la comprensión del código. También hemos movido código entre clases, delegando responsabilidades entre objetos. Y finalmente, en la última entrega dejamos unas condiciones múltiples (switch) en los métodos getCharge y getFrequentRenterPoints en la clase Movie. Vamos a ver en este final del ejemplo cómo eliminar esos ifs, lo cuál me sirve de entradilla para comentar la campaña anti-if (en italiano, sí, en italiano).

Comenzamos.

At last… Inheritance

Para no plagiar, creo que lo mejor es recomendar directamente la lectura de este capítulo 1 del libro. Sin embargo, y aunque la explicación es excelente, hay que reconocer que el original está en inglés… :-)

Aunque no me dedico a la traducción y mi inglés es mejorable, voy a tratar de aportar mi granito de arena a la causa hispana.

AL FIN… HERENCIA

Tenemos varios tipos de películas que tienen diferentes maneras de responder a la misma pregunta. Esto parece un trabajo para subclases. [N.T. El autor se refiere a Movie.getCharge]. Podemos tener 3 subclases de Movie, cada una de las cuáles teniendo su propia versión de getCharge. (Ver Fig. 1.14)

[N.T. La fig. 1.14 muestra un diagrama de clases UML con las subclases RegularMovie, ChildrensMovie y NewReleaseMovie, heredando de Movie. Todas tienen un método llamado getCharge.]

Esto nos permite sustituir la instrucción switch por el uso de polimorfismo. Desgraciadamente esto tiene un pequeño problema: que no funciona. Una película puede cambiar su clasificación a lo largo de su vida. Un objeto no puede cambiar su clase a lo largo de su vida. Sin embargo, hay una solución en el patrón Estado [GoF]. Usando el patrón Estado, nuestro diseño quedaría como en la fig. 1.15

[N.T. La fig. 1.15 muestra otro diagrama de clases UML con la clase Price y sus subclases RegularPrice, ChildrensPrice y NewReleasePrice. Además, también muestra la clase Movie usando una instancia de Price para el cálculo de getCharge.]

Añadiendo la indirección podemos hacer las subclases del objeto Price y cambiar el precio cada vez que lo necesitemos.

Si estáis familiarizados con los patrones del GoF, quizás os preguntéis: “¿Es esto un estado o una estrategia?”. ¿Representa la clase Price a un algoritmo para calcular el precio (en cuyo caso yo preferiría llamarla Pricer o PrincingStrategy) o representa al estado de una película (Star Trek X es una novedad)? En este punto, la elección del patrón (y del nombre) refleja cómo queremos pensar acerca de la estructura. De momento estamos pensando en este objeto como el estado de una película. Si más adelante decidimos que una estragia comunica mejor nuestras intenciones, refactorizaremos cambiando los nombres.

Hasta aquí la traducción. Espero que os ayude a entender el cambio que vamos a introducir en el diseño y el por qué lo hacemos.

Vamos a introducir el patrón Estado. Para ello nos aseguramos de que tenemos bien encapsulados los accesos al atributo Movie._priceCode. (Lo cambiamos incluso en el constructor).

TESTS

Para introducir Price y toda la jerarquía hacemos TDD y, para ello, creamos PriceTest como sigue y lo añadiremos a la suite TestAll.

package step6;

import org.junit.Test;

public class PriceTest {

 @Test public void testChildrensPrice() {  Price aPrice = new ChildrensPrice();  assertEquals(Movie.CHILDRENS,aPrice.getPriceCode()); }

}

Lógicamente, como no compila, tendremos que crear las clases y métodos correspondientes. Al ejecutar los tests, salen en rojo (si hemos implementado getPriceCode con un valor por defecto), pero en cuanto hacemos que getPriceCode devuelva el valor esperado según la subclase que vayamos incorporando, volvemos al verde. Siempre primero el test y luego el código.

A continuación introducimos el uso de Price en Movie tal y como propone Fowler. De hecho, al copipegar su implementación de Movie.setPriceCode incorpora código para el que no teníamos pruebas. Esto lo descubrimos al ver el informe de cobertura de nuestras pruebas y comprobar que no tenemos una prueba para comprobar qué pasa cuando asignamos a una película un tipo de precio desconocido. (Sí, en sentido estricto no estaríamos refactorizando puesto que hemos cambiado los tests, e.d. la funcionalidad original, pero yo más bien diría que hemos encontrado un defecto en el código original puesto que no contemplaba una condición excepcional).

 @Test(expected=IllegalArgumentException.class) public void testMovieIllegalType() {  new Movie(ANY_TITLE,UNKNOWN_PRICE_CODE); }

Por lo demás, tras ejecutar nuestra batería de tests, el refactor ha ido bien. (Lo sabemos porque nuestros tests están en verde)

Siguiendo los pasos de Fowler, refactorizamos Movie.getCharge para que use un nuevo Price.getCharge (OJO, sin olvidar que estamos haciendo TDD) :-)

Implementamos Price.getCharge moviendo el código de Movie.getCharge a la clase abstracta (aunque este método no será abstracto).

No eliminamos el viejo test puesto que es lo que nos indicará que funciona o no nuestro refactor; pero implementamos los tests como el siguiente. (En realidad es fácil porque son idénticos a los de Movie)

 @Test public void testChargeForChildrens() {  Price aPrice = new ChildrensPrice();  double charges[] = { 1.5, 1.5, 1.5, 3.0, 4.5, 6.0, 7.5 };  for (int daysRented = 1; daysRented <= charges.length; daysRented++) {    assertEquals(charges[daysRented-1], aPrice.getCharge(daysRented),0);  } }

TESTS

Declaramos el método ahora como abstracto e implementamos el primero que nos venga en gana (p.ej. ChildrensPrice.getCharge). Ya tenemos los tests, por lo que iremos refactorizando con seguridad. Recordad el lema de TDD: “RED GREEN REFACTOR“.

TESTS

A continuación, vamos haciendo los cambios correspondientes al resto de tipos de precio. Siempre con pasos seguros gracias a nuestra batería de tests.

TESTS

Y finalmente hacemos lo mismo con getFrequentRenterPoints:

  • movemos el código de Movie.getFrequentRenterPoints a Price.getFrequentRenterPoints (incluyendo primero los nuevos tests porque hacemos TDD)
  • no olvidamos mover los javadocs
  • implementamos el caso particular de NewRelease.getFrequentRenterPoints (dejando el caso general en la clase abstracta Price)

Pues bien, llegados a este punto ya hemos terminado con el ejemplo de Fowler. Os recomiendo encarecidamente el libro (en él se detallan las técnicas de refactor y los “tufillos” (smells) que nos indican cuándo es posible que nuestro código requiera ser refactorizado) y un par de secuelas muy interesantes:

  • Refactoring Workbook, con el que podréis ejercitar todo lo que hayáis aprendido con el de Fowler.
  • Refactoring to Patterns, con el que podréis orientar vuestros refactorajes hacia patrones de diseño, mejorando así la calidad de vuestros diseños.

Finalmente, una última recomendación: http://sourcemaking.com/refactoring

Desgraciadamente todas estas referencias están en inglés. :-(
¿Cuándo nos decidiremos a crear contenidos en español ahora que estamos reivindicando la profesión aquí en España?

Tags: ,

19 SepEjercicios de TDD

Estaba revisando las últimas entradas de la lista de Test-driven Development y me he encontrado con un sitio muy simple pero muy útil (especialmente para los que queremos aprender a hacer TDD). Recomiendo seguir los enlaces hasta un ejercicio de Robert C. Martin (Uncle Bob) que resulta de lo más didáctico. Yo mismo voy a seguirlo paso a paso este fin de semana (lo pongo en mi lista con una prioridad alta).

Tags:

08 AugCode Coverage en Eclipse

Aunque se supone que estoy de vacaciones… no he podido resistir el hacer un ejercicio (que publicaré en breve en este blog) sobre Refactoring. Pero quería ver cómo de buenos eran mis tests y, la verdad, no me siento cómodo con el plugin de Cobertura para Maven: quería algo más integrado con Eclipse. Para ello he estado mirando los proyectos TPTP de Eclipse y EclEmma. Ambos están basados en Emma.

Después de buscar y buscar cómo instalar el módulo de code coverage de TPTP me he encontrado con que han abandonado el desarrollo para usar justamente EclEmma. Bueno, eso me ha simplificado el trabajo. :-)

Así pues, después de instalar EclEmma, probar la cobertura de mis tests JUnit ha sido muy fácil. Simplemente he seguido las instrucciones. (Ejem, he de reconocer que las seguí después de ver que me estaba calculando la cobertura ¡¡de los propios tests!!)

Para que no incluya nuestros tests en el informe de cobertura hay que:

  • pulsar en el menú “Run / Coverage”,
  • seleccionar la configuración de nuestro test,
  • pulsar en la pestaña “Coverage”
  • y desmarcar la carpeta correspondiente (en mi caso src/test/java porque uso Maven)

Como suponía, mi cobertura era del 100%. :-)

De todos modos, no tener un plugin estable de Emma para Maven2 hace difícil el incorporarlo al proceso de integración continua (puesto que yo he optado por tenerlo todo con Maven2 y no usar Ant ni otros procesos añadidos). En cualquier caso, en cuanto pueda lo probaré a ver qué tal…

P.S.
Si alguien tiene interés, incluyo el enlace a una presentación sobre EclEmma de la EclipseCon 2008 que puede aclarar cómo funciona por dentro (incluidas referencias a OSGi).

08 JulDe mayor quiero ser como Kent Beck

Dada mi nueva situación (perdonad el eufemismo, pero es la moda…), estos días estoy trabajando en mi CV. Para ello he visitado los perfiles que otros profesionales tienen en LinkedIn. Y en esta labor de cotilleo estaba cuando dí con el perfil del mismísimo Kent Beck (autor de libros como el muy recomendable “Implementation Patterns”, frameworks tan usados como JUnit o tendencias tan reconocidas como el Test-Driven Development). Mi admiración por él ha aumentado aún más cuando he leido su resumen. Tanto es así que lo he incorporado a mi propio perfil (sin quitarle ni una coma).

Espero que Mr. Beck me disculpe por el plagio, pero se trata, además de un homenaje, de una identificación total con cada una de sus palabras.

07 AprAcceptance Test Driven Development

El miércoles tengo que impartir un taller de TDD (Test Driven Development) en DEGESYS y, aunque parto ya del mismo taller que impartí hace ya varios meses, quería buscar nuevas ideas para enriquecerlo y he encontrado esta presentación bastante interesante.

Por cierto, el libro de Manning sobre TDD y Acceptance TDD es también excelente.

Tags: ,