Capitulo 4: La Privacidad y El conceto


Buenas!

Hoy vamos a hacer el testMultiplication() un poco más significante. Que cumpla más con el concepto que se le supone.

public void testMultiplication() {

    Dollar five= new Dollar(5);
    Dollar total = five.times(2);
    Assert.assertEquals(10, total.amount);
    total = five.times(3);
    Assert.assertEquals(15,total.amount);
   }
}

Lo que aquí comparamos es una propiedad del Dollar con un número pero lo que queremos hacer es comparar cantidades donde el Dollar es la unidad. No deberíamos navegar por dentro de las propiedades de ningún objeto para hacer nuestra comparación. Más genéricamente no deberíamos nunca tener acceso a una variable de un objeto, no directamente. Pero lo que por ahora nos interesa es tener un test que refleje mejor lo que nosotros queremos testear. Bueno, va a ser fácil después de todos los cambios que hemos venido haciendo en el Dollar. El método times() devuelve Dollar así que podemos comparar dólares con lo que sale de este método.

@Test
public void testMultiplication() {
   Dollar five= new Dollar(5);
   Assert.assertEquals(new Dollar(10), five.times(2));
   Assert.assertEquals(new Dollar(15), five.times(3));
}

Test: Process finished with exit code 0. Verde.

Voilá.

t Hacia tiempo que en el TODO teniamos algo pendiente. Hacer amount private y ahora ya no tenemos acceso a amount en el test así podemos hacerlo sin problemas

class Dollar {

    private int amount;

    Dollar(int amount) {
        this.amount = amount;

    }

    Dollar times(int multiplier) {
       return new Dollar( amount * multiplier);
    }

    public boolean equals(Object object) {
        Dollar dollar = (Dollar) object;
        return amount == dollar.amount;
    }

Test: ….verde.

TODO

  • 5 $ + 10 CHF = 10 $ si el ratio es 2:1 de  CFH:$
  • 5 $ * 2 = 10 $
  • Hacer “amount” private
  • Comprobar efectos colaterales de “Dollar”
  • Redondeo de moneda
  • equals()

 

Pues nada el capitulillo de hoy no dá para mucho, en el próximo empezamos con una nueva moneda…el franco.

Publicado en TDD | Etiquetado , , , | Deja un comentario

Capitulo 3: Triangulación


Buenas.

Recordando el capítulo anterior cabe comentar que trabajamos con el patrón Object Value,  que hace de un objeto un valor. Esto viene a ser que en el constructor definimos ciertos valores que una vez creado el objeto no pueden modificarse. Supongo, si habéis leído el capítulo anterior, esto os suena a algo. Recordemos como en ese capítulo teníamos ciertos problemas para multiplicar (times()) varias veces el mismo objeto Dollar que inicializabamos a un valor de 5. Así que decidimos evitar que ningún método pudiera modificar el amount inicial. En cuanto a esto nada más que añadir.

Hoy hablaremos del concepto triangulación en TDD a través de la comparación de dos objetos dollar.

TODO

  • 5 $ + 10 CHF = 10 $ si el ratio es 2:1 de  CFH:$
  • 5 $ * 2 = 10 $
  • Hacer “amount” private
  • Comprobar efectos colaterales de “Dollar”
  • Redondeo de moneda
  • equals()

Antes de nada probemos a comparar dos objetos.

 @Test
 public void testEquality() {
   Assert.assertTrue(new Dollar(5).equals(new Dollar(5)));
}

Process finished with exit code 255; error

No son el mismo objeto. Era de esperar.

Si bien necesitamos que el resultado de la prueba sea positivo, no por que estos dos dollares sean el mismo objeto sino por que dos monedas iguales han de valer lo mismo. Sobrescribamos en Dollar el método equals() para pasar al verde.

public boolean equals(Object object) {
   return true;
}

Test: Process finished with exit code 0; correcto.

Pero qué pasa si probamos

 public void testEquality() {    ;
   Assert.assertTrue(new Dollar(5).equals(new Dollar(5)));
   Assert.assertFalse(new Dollar(5).equals(new Dollar(6)));
}

Test: Process finished with exit code 255; error

Esto es triangular. El primer paso, vamos. Si de dos casos sacamos un concepto que auna a ambos, triangulamos. A mí en particular este nombre no me parece adecuado pero nomenclaturas aparte lo importante aquí es ver con claridad que  tenemos que agrupar ambos casos en un concepto que los abarque a ambos de manera apropiada. Decimos que no valen lo mismo un billete (moneda? cheque?….) de 5 dóllares que uno de 6, pero sí dos de 5  y bajo estas afirmaciones late el concepto que buscamos. Lo que comparamos en estos dos casos es el amount de dos objetos diferentes. Así se cierra el …triángulo. Implementémoslo.

  public boolean equals(Object object) {
        Dollar dollar = (Dollar) object;
        return amount == dollar.amount;
    }

Test: Process finished with exit code 0 ; correcto.

Esto es lo que buscábamos. Así que lo que hoy hemos visto que la triangulación es aquel método que nos enseña a pedir a través del test implementaciones que el TDD necesita para explicitar ciertos requisitos de nuestro desarrollo. Era evidente que la primera implementación de equals() no podía ser acertada pero no hemos de olvidar que estamos desarrollando orientándonos a las pruebas y lo que no salga de ellas no tiene, en principio, que salir de ningún sitio. Esta es la teoría pero nos confiesa el maestro Beck que el solo utiliza este método cuando tiene serias dudas de como implementar un método. El resto de las veces tira el código que cree que es correcto y no mira pelo. A ver quien se atreve a decirle na. 😉

Un saludo, hasta la próxima!

Publicado en TDD | Etiquetado , , , | Deja un comentario

Capitulo 2, Kent Beck: Degeneración de Objetos


Hola de nuevo.

En el capitulo anterior hicimos todo el desarrollo al revés de lo habitual. Tiramos código única y exclusivamente para que pasara nuestro test. Hoy sin perder eso de vista volveremos a preocuparnos por un código limpio. Sumaremos al TDD el desarrollo orientado a arquitectura tirando aquel código que pensemos que es evidente.

TODO

  • Hacer “amount” private
  • Comprobar efectos colaterales de “Dollar”
  • Redondeo de moneda

Comprobemos que pasa con Dollar cuado efectuamos diferentes operaciones sobre el mismo.

import org.junit.Test;

import static junit.framework.Assert.assertEquals;

public class CurrencyTester {

@Test
public void testMultiplication() {

    Dollar five= new Dollar(5);
    assertEquals(10, five.amount);
    total = five.times(3);
    assertEquals(15,five.amount);
    }
}

Test:

Process finished with exit code 255
junit.framework.AssertionFailedError: expected:<15> but was:<30>

Vemos que al multiplicar de nuevo el Dollar el resultado que nos da depende de la multiplicación anterior. ¿Es eso lo que queremos? Yo diría que no. Lo que el test prueba es que al multiplicar un número de productos por un precio obtengamos un total. ¿Cómo lo solucionamos? Veamos. ¿Qué significa que multipliquemos un Dollar por una cantidad? Significa que el resultado de la operación es un total, un total que ha de expresarse en alguna unidad. La unidad del Dollar es…un dollar, así que parece lógico que lo que produzca esta operación sea dollares, no?

Implementémoslo en un test:

   public void testMultiplication() {

    Dollar five= new Dollar(5);
    Dollar total = five.times(2);
    assertEquals(10, total.amount);
    total = five.times(3);
    assertEquals(15,total.amount);
    }
}

No compilará. Ejecutamos el test:

incompatible types

found   : void

required: tests.Dollar

Tenemos que cambiar la salida de times(). Recordemos, paso a paso.

Dollar times(int multiplier) {
    amount *= multiplier;
   return null;
}

Test:

Process finished with exit code 255
java.lang.NullPointerException

Compila pero falla. si ahora:

Dollar times(int multiplier) {
       return new Dollar( amount *= multiplier);
    }

Test:

junit.framework.AssertionFailedError: expected:<15> but was:<30>

¿??¿¿?

Ah!! sobra el igual:

 Dollar times(int multiplier) {
       return new Dollar( amount * multiplier);
    }

Test:

Process finished with exit code 0

TODO

  • Hacer “amount” private
  • Comprobar efectos colaterales de “Dollar”
  • Redondeo de moneda

Buenos yo creo que con este capitulo y el anterior ya tenemos claro que hay una forma de implementación que es mínima en cuanto a riesgo pero también mínima en cuanto a avance. Podemos, sin embargo, tirar un poco más de código, sin poner constantes como el null de times() o amount=10 que poniamos el otro día. El caso es que la barra del test permanezca verde. Si hay un código obvio que implementa nuestras necesidades es recomendable que lo escribamos sin olvidarnos de nuestro color favorito… Si la barra se torna roja debemos dejar la creatividad para otro momento.

El proximo capítulo veremos una tercera forma de implementación. Triangular.

Hasta pronto.

Publicado en TDD | Etiquetado , , , | Deja un comentario

Capitulo1, Kent Beck: Mantra


Buenas de nuevo, hoy vamos a familiarizarnos con el mantra TDD. Rojo, Verde, Refactorización.

En TDD la metodología a seguir es escribir primero un test, y que falle, rojo, luego tirar código para que pase, verde, y en última instancia, refactorizar el código de las clases que estamos probando para eliminar su posible y probable dependencia con los test. Vayamos por partes.

Como decíamos el día anterior, el equipo de Kent Beck se enfrenta a un cambio que implica trabajar con todo tipo de monedas. En principio solo trabajaban con dólares, ahora trabajan con algo más abstracto, monedas.

Por otra parte utilizaremos como en el libro una sencilla y útil metodología: Ponemos en una lista (TODO que ha de leerse en inglés y separado, To do) nuestros objetivos funcionales, en negrita aquel con el que nos enfrentamos en cada momento y tachado cuando lo terminemos.

Queremos verificar que podemos sumar francos suizos y dólares en los informes que genera la aplicación, pero la pregunta y su solución son más genéricas. ¿Podemos sumar diferentes monedas mediante una tasa de equivalencia? Por otro lado, queremos poder multiplicar una cantidad de productos por su precio y obtener un resultado, un total. Por esto la siguiente lista:

TODO

  • 5 $ + 10 CHF = 10 $ si el ratio es 2:1 de  CFH:$
  • 5 $ * 2 = 10 $

Vamos al tajo,  rezando el nuevo mantra.

Rojo:

Creamos la clase que  pruebe que podemos multiplicar un precio por una cantidad.

import org.junit.Test;

import static junit.framework.Assert.assertEquals;

public class CurrencyTester {

@Test

public void testMultiplication() {

Dollar five= new Dollar(5);

five.times(2);

assertEquals(10, five.amount);

}

}

Fácil, no? Si lanzamos el test, aunque el editor ya nos avisa de que esto no va a compilar, nos salta los errores:

(17,8) cannot find symbol class Dollar.

(17,25)cannot find symbol class Dollar.

Verde:

Necesitamos que compile. Creamos la clase Dollar con un constructor que acepte int. Este int será su atributo amount, y por último, creamos el método times. Solo pensamos en pasar el test. Es él quien dirige el desarrollo, lo que él no nos pida  lo implementamos. Esta es la esencia del TDD.

class Dollar {

int amount;

Dollar(int amount) {   }

void times(int multiplier) {   }

}

Ejecutamos lo que ahora tiene que compilar y vemos que efectivamente compila, pero que el test sigue fallando.

Process finished with exit code 255

junit.framework.AssertionFailedError: expected:<10> but was:<0>

Lo siguiente es un golpe bajo al estilismo, pero nosotros a lo nuestro. VERDE

class Dollar {

int amount=10;

Dollar(int amount) {  }

void times(int multiplier) {    }

}

Ejecutamos el test y, voila.

Entiendo que tirar un código así pueda dar grima. A mi me la da pero hemos de esforzarnos en adquirir una perspectiva diferente. A la larga nos lo agracecerá nuestro pelo.

Refactorización:

Test y código testeado deben evitar la dependencia, que se muestra generalmente en la forma código duplicado. Si estoy haciendo una conexión a una base de datos de MySQL he de evitar que el test se vincule directamente a esta. He de testear una conexión genérica, es decir, crear una conexión génerica sobre la que actuarán los test. Si quiero que mis monedas multipliquen adecuadamente he buscar una multiplicación genérica. Nuestro código duplicado es aquí el 10 del resultado. Podíamos eliminarlo con otra obviedad.

int amount=5*2;

o bien con

int amount;

Dollar(int amount) {amount = 5*2 };

Pasamos el test. Verde.

Aunque el cambio pueda parecer absurdo en un principio, debemos ir paso a paso. No conviene hacer un cambio que no sea testeado. Siguiente refactorización. Estamos duplicando el 5  del test en el código, eliminémoslo.

int amount;

Dollar(int amount){

this.amount = amount;

}

void times (int multiplier){

amount = amount*2;

}

Hemos eliminado el 5 y con el su dependencia en el código. Testeamos…verde

¿Alguna otra duplicación? …el 2:

void times(int multiplier){

amount = amount*multiplier;

}

o lo mismo pero más elegante:

void times (int multiplier){

amount *=multiplier;

}

Testeamos. Verde.

Listo, pues.

TODO

  • 5 $ + 10 CHF = 10 $ si el ratio es 2:1 de  CFH:$
  • 5 $ * 2 = 10 $

Supongo que sorprende esta forma de trabajar, no parece muy intuitiva. Con razón decía Kent Beck que nos encontraríamos con varias sorpresas entre las que probablemente se incluyen:

  • Cómo cada test cubre una pequeño aumento de funcionalidad
  • Cuan pequeños y monstruosos son los cambios que se pueden llegar a hacer para que un test corra
  • Cuan a menudo se ejecutan los test
  • Cuantos minúsculos cambios incluye la refactorización

Hasta aquí el capitulo 1, del cuál creo que el concepto clave es que el desarrollo es TODO Tests.

La recomendación del día es que si estamos trabajando y viene un funcional que ha tomado los requisitos del cliente, nuestra mejor opción es cumplirlos a través de las pruebas. Nosotros hemos de transformar las necesidades del cliente en los tests adecuados. Esta es la forma más segura de implementación. TDD produce mejor código en menos tiempo. A los no creyentes propongo rezar el mantra.

Un saludo.


Publicado en TDD | Etiquetado , , | Deja un comentario

Albricias


Bueno, aquí estoy escribiendo, por fin, algo sobre software. Ya tenía ganas. El caso es que me he decidido por el desarrollo orientado a pruebas. Test Driven Development, todo un mundo. Cuanto más leo más me doy cuenta de lo necesario que es. Lo que no tengo muy claro es como afrontar este blog.

Por lo que yo he visto, a nadie- programadores , se entiende – le gusta escribir tests. El caso es palmario. Uno prefiere terminar con la funcionalidad a implementar, probarla y …listo. Ya pasará al entorno de pruebas y se levantarán las incidencias. La idea en principio puede no parecer mala. Muchos clientes también opinan igual. Imaginaros. El desarrollo de las pruebas también hay que pagarlo. ¿No podríamos probarlo con las manitas?¿Para que está el entorno de Test?

Hay infinidad de razones para realizar las pruebas, previas al desarrollo. A mi me obsesionan algunas.

  • Sabes cuando has terminado el desarrollo sin apenas tener que usar las manitas.
  • Más aún, cuando un nuevo desarrollo de funcionalidad implica la modificación de código sabes si no has tocado lo que no debías. Esta es mi cruz.
  • Se minimizan los fallos una vez entregada la herramienta al cliente. En el entorno de test son usuarios los que se supone que van a probarlo. Cuanto menos incidencias menos… incidentes.

En el entorno de la informática, hay un dicho que oigo allí donde voy. !No reinventes la rueda! ¡No reinventemos la rueda! A mi hace tiempo que me han convencido. Me subiré al carro y empezaré siendo muy poco original. Resumiré un clásico. Test Driven Development: By Example, de Kent Beck.

En este archiconocido libro se nos plantea un problema en una aplicación tras dos de años de desarrollo. Viernes por la mañana, el finde se acerca.  Aparece Peter, The Big Boss, mal asunto.  Reunión. La herramienta es lider del mercado. Todo va sobre ruedas, pero ya se sabe, renovarse o morir. Estamos pensando en una nueva estrategía pero tendriamos que empezar a trabajar no solo con Dollares sino con otras monedas. Podemos hacerlo? pregunta. El director de desarrollo de software, Ward, no está seguro de la respuesta…. Estas preguntas suelen provocar una variada y desagradable sintomatología… Madre del amor hermoso!! un cambio que atraviesa toda la herramienta.

Pasa el fin de semana y Big Boss vuelve el lunes. ¿Bueno, podemos hacerlo?. Y aquí empieza la epopeya. Dame un día más y te lo digo con seguridad, responde Ward.

Es Ward un Jedi? tiene contactos con el Altísimo? Un día, dice, y te lo digo con seguridad. Esto es un Gurú y lo demás son tonterías.

Mr Pro decide que se hagan pruebas con uno de los algoritmos más complicados del sistema que calcula medias ponderadas. Si podemos hacerlo para este algoritmo podremos hacerlo para toda la aplicación, él sabe. Y aquí aparece la herramienta divina. Los Tests. El equipo hace los cambios  oportunos, pasan los test. y fallan pocos. Pulen el código y voilá. Al final del día tienen la respuesta. Yes, we can.

Para mi esto es inspirador. Yo quiero trabajar con ese grado de confianza en mi desarrollo y, como no,  con esa rapidez. Si vosotros también quizá aquí encontréis algo que os sirva. Pero no en el post de hoy. El próximo día os cuento los principios básicos y empezamos a tirar código. Teoría y practica. O en la medida de lo posible al revés, que es más natural y productivo, aunque menos habitual en el mundo académico. Ya veremos.

Un saludo,

Francisco López-Sancho Abraham

Publicado en TDD | Etiquetado , , | Deja un comentario