Vous êtes sur la page 1sur 117

1

Desarrollo Dirigido por


Pruebas Prctico
Javier J. Gutirrez Rodrguez
Prototipo
tdd@iwt2.org
Estamos en:
http://www.iwt2.org/web/opencms/IWT2/comunidad/LibroTDD/?locale=es
2




3








Twitter: @TDDPractico
E-mail: TDD@iwt2.org
Encuesta on-line (va Google):
http://goo.gl/mz6by
4

Control de cambios.

Fecha Versin Cambios
20/04/2013 Prototipo 01 Primer prototipo pblico.
28/04/2013 Prototipo 02 Incluimos captulo 03.
Incluimos ndice y enlace de la encuesta
Corregimos algunos errores en el captulo 1 gracias a
Guillermo Gutirrez (va ScribD).
19/05/2013 Prototipo 03 Incluimos captulo 6 (incompleto) y un cuestionario de
autoevaluacin sobre conocimientos de testing.
Arreglamos el ndice para que muestre correctamente los
ttulos de los captulos.
01/06/2013 Prototipo 04 Mejoramos los textos de los captulos 1 y 2.
Aadimos un apartado de agradecimientos del libro (adems
de los agradecimientos de cada captulo).
Aadimos una descripcin del contenido de los captulos 4 y
5. Comentarios son bienvenidos.
03/07/2013 Prototipo 05 Aadimos el captulo 4 completo con dos soluciones distintas
para la kata Mancala.
15/11/2013 - Prototipo 06 Revisamos el captulo 1.
Revisamos el captulo 2 e incluimos un ligero sombreado en
los cuadros de cdigo que contiene cdigo de prueba.
Revisamos el captulo 3.
Aadimos el captulo 7 completo.
Aadimos el apndice II completo
Se modifica ligeramente el formato del ndice para resaltar
los captulos.

5

Introduccin al proyecto

Alberto Savioa (@Pretotyping en Twitter) escribi un interesante y muy recomendable
libro sobre prototipos y que, a su vez, tambin es un prototipo de libro (Pretotype It:
http://www.pretotyping.org/pretotype-it---the-book). He decidido adoptar esa misma
filosofa en el futuro libro que, sin embargo, ya ests leyendo justo en este momento.
Tengo muy claro que no quiero escribir un libro solo para plasmar mis
conocimientos de TDD o mi visin particular. Existen algunos libros muy buenos que
explican casi todo lo que necesitas saber, y el resto te lo da la prctica. Mi objetivo es
ensear TDD a travs de ejemplos que muestren muchos de los escenarios y dudas que
aparecen cuando se aplican y que ofrezcan ideas y soluciones para abordarlos.
Todo el material que se elabore para este libro ser de libre descarga lo ms
rpidamente posible. Creo que vale la pena que el material llegue pronto a los
interesados, aunque eso implique que incluya notas de cosas por hacer y arreglar o que
el formato no est pulido. Mi objetivo es que quin tenga inters, pueda ver cmo va a
ser el libro y pueda anticiparse y contarme qu cosas quiere que ponga o cambie. As,
tendremos un libro de todos para todos.
Voy a desarrollar el libro en dos fases: prototipos y versiones beta.
Los prototipos sern versiones del libro incompletas. Incluso con captulos que
tampoco estn completos. El contenido de un prototipo no es estable y todo puede
cambiar o desaparecer en futuras versiones. Probablemente la versin que ests
leyendo ahora mismo sea un prototipo.
Una vez que encontremos la estructura del libro adecuada, los ejemplos ms
interesantes y la mejor manera de exponerlos, pasaremos a las versiones beta. Una
versin beta ya ser un libro completo y cerrado con todos (o casi todos) sus captulos.
El objetivo de las versiones beta ser pulir el contenido y corregir erratas.
Adems, tambin espero que esta filosofa de liberar el trabajo rpidamente me
permita obtener un feedback que me anime y me motive a trabajar en el libro.
No quiero despedirme sin agradecerte la ayuda que me ests prestando slo por
leer estas lneas. Espero que lo que encuentres aqu te resulte til y entretenido.

Javier Jess Gutirrez
Sevilla Primavera 2.013

6

Agradecimientos
Hay muchas personas que me han ayudado de una u otra manera a que este libro vaya
creciendo y, sobre todo, se vaya adaptando a los gustos y expectativas de vosotros, sus
lectores. Como justo reconocimiento de toda esa ayuda he querido poner en cada
captulo el nombre de las personas y proyectos que han estado involucrados.
Pero tambin he tenido la enorme suerte de encontrar a personas que se merecen
ser citadas en todos y cada uno de los captulos. Va para ellos tambin mi
agradecimiento.
A Joaqun Engelmo Moriche (Kinisoft) por sus muchos y muy buenos
comentarios durante los primeros prototipos del libro, que eran justo cuando
ms lo necesitaba.
A Roberto Fernndez Leis por sus valiosos, detallados y extensos comentarios
de todos los captulos del prototipo 5.



7

ndice

Captulo 1. Desarrollo Dirigido por Pruebas. Lo Mnimo que necesitas saber...... 10
Un ejemplo de Test-Driven Development .................................................................. 11
El diario de diseo ..................................................................................................... 12
El ciclo de Test-Driven Development ......................................................................... 13
Cdigo mnimo para pasar una prueba ...................................................................... 14
Empezar fallando ...................................................................................................... 15
La importancia de la refactorizacin de todo el cdigo .............................................. 15
Cmo continuar aplicando el ciclo TDD ..................................................................... 17
Heursticas TDD ......................................................................................................... 17
Formato del cdigo ................................................................................................... 18
Para terminar ............................................................................................................ 18
Enlaces y referencias ................................................................................................. 19
Agradecimientos del captulo .................................................................................... 19
Captulo 2. Primeros ejemplos de desarrollo dirigido por pruebas ...................... 20
Listas palndromas (en Java) ...................................................................................... 20
Tipo de dato lista (en Python).................................................................................... 24
Para reflexionar ........................................................................................................ 26
Solucin al ejercicio planteado .................................................................................. 27
Agradecimientos del captulo .................................................................................... 28
Captulo 3. Criba de Eratstenes en Java............................................................ 29
El problema ............................................................................................................... 29
Empezamos por un mal camino................................................................................. 29
Un nuevo comienzo .................................................................................................. 31
Integrando ................................................................................................................ 35
Conclusiones ............................................................................................................. 36
Para reflexionar......................................................................................................... 36
Captulo 4. Dos soluciones para el Mancala ....................................................... 38
El problema ............................................................................................................... 38
Cada semilla en su pozo ............................................................................................ 40
8

Sembrar para el futuro .............................................................................................. 43
Haced sitio ................................................................................................................ 44
Una pausa para refactorizar ...................................................................................... 45
Nada que plantar ...................................................................................................... 46
De vuelta a la choza .................................................................................................. 47
No uses las chozas..................................................................................................... 48
De la ltima a la primera ........................................................................................... 49
La ltima refactorizacin ........................................................................................... 50
Retrospectica de la primera solucin ......................................................................... 52
Un nuevo comienzo. ................................................................................................. 55
Nuestra segunda primera prueba. ............................................................................. 55
Un error comn. Triangular el fake ............................................................................ 56
Refactorizar y avanzar ............................................................................................... 57
Triangulando la siembra ............................................................................................ 58
Triangulando pruebas y cdigo.................................................................................. 60
Implantando el segundo jugador ............................................................................... 62
Del final al principio .................................................................................................. 64
Solo pozos vlidos ..................................................................................................... 65
Retrospectica de la segunda solucin ........................................................................ 66
Comparacin entre ambas soluciones. ...................................................................... 67
Para reflexionar ........................................................................................................ 67
Conclusiones ............................................................................................................. 68
Agradecimientos del captulo .................................................................................... 68
Captulo 5. Ejercicio TDD para presentar y usar mocks / doubles, etc. ................ 69
Captulo 6. Cifrado Esctala y Refactoriacin ..................................................... 70
El Problema ............................................................................................................... 70
El Cdigo ................................................................................................................... 70
Podemos leerlo? ..................................................................................................... 72
Escribiendo Pruebas .................................................................................................. 75
Desbloqueando a los mtodos principales. ................................................................ 77
Cambiando mensajes por excepciones ...................................................................... 79
Conclusiones ............................................................................................................. 82
Agradecimientos del captulo .................................................................................... 82
9

Captulo 7. Suscriptores y Dobles de Prueba ...................................................... 83
El problema ............................................................................................................... 83
La primera prueba de subscripcin ............................................................................ 85
Dos son multitud ....................................................................................................... 87
Refactorizando pruebas repetidas ............................................................................. 89
Completando las subscripciones................................................................................ 90
Refactorizando las pruebas ....................................................................................... 92
Un nuevo husped .................................................................................................... 93
Hora de borrar .......................................................................................................... 96
Solo cuando de verdad se borre ................................................................................ 97
Conclusiones ............................................................................................................. 98
Anexo 1. Test de pruebas ................................................................................... 99
Preguntas .................................................................................................................. 99
Respuestas .............................................................................................................. 103
Agradecimientos del captulo .................................................................................. 103
Anexo 2. Dobles de Prueba y Mockito ...............................................................104
Dobles de prueba .................................................................................................... 104
Tipos de dobles de prueba ...................................................................................... 106
Una breve introduccin a Mockito .......................................................................... 108
Ejemplos con Mockito ............................................................................................. 108
Errores comunes trabajando con dobles de pruebas ............................................... 112
Alternativas a los dobles de prueba ......................................................................... 112
Para terminar .......................................................................................................... 114
Bola Extra. FitNesse ................................................................................................. 114
Referencias ............................................................................................................. 114
Agradecimientos del anexo ..................................................................................... 115


10



Captulo 1.
Desarrollo Dirigido por Pruebas. Lo
Mnimo que necesitas saber


Como veremos ms adelante, uno de los pilares del desarrollo dirigido por pruebas (TDD
por sus siglas en ingls) es escribir siempre el mnimo cdigo posible para que una
prueba se ejecute con xito. En este libro vamos a aplicar la misma filosofa a la teora
de TDD, por eso en este captulo encontrars los conceptos mnimos necesarios para
empezar a aplicar TDD a partir del captulo siguiente.
No vas a encontrar aqu reflexiones sobre la bondad o no de TDD, ni discusiones
filosficas sobre si TDD est ms centrada en el diseo que en pruebas, o cualquier otro
tema habitual de debate sobre TDD porque damos por hecho que, si lees esto, ests lo
suficientemente convencido para darle una oportunidad a TDD. Solo lo mnimo para
entrar en accin.
Aun as, si este captulo se te hace aburrido, no lo leas. Ve directamente a los
ejemplos y comienza a practicar TDD. Vuelve aqu de vez en cuando para repasar y
terminar de afianzar los conceptos bsicos. Recuerda que para aprender bien TDD hay
que practicarlo.
Como complemento a estas pldoras de teora de TDD, encontrars al final de
cada ejemplo prctico reflexiones sobre la manera de aplicar TDD que te ayudar a
profundizar en esta buena prctica.


11


Para empezar a aprender y practicar TDD solo necesitas conocimientos bsicos sobre
prueba del software. Hemos incluido un conjunto de ejercicios para que tambin puedas
practicar y evaluar tus propios conocimientos. Encontrars estos ejercicios en los
apndices de este libro. Cuanto ms aprendas y practiques sobre prueba del software
mejor aplicars TDD.


Un ejemplo de Test-Driven Development
Empecemos con un ejemplo. Supongamos que estamos escribiendo una clase que
funciona como un carrito de la compra que an no ha sido escrita. Como queremos
aplicar TDD debemos escribir una prueba antes de poder empezar a implementar la
clase. Un posible caso de prueba podra ser el mostrado en el cdigo 1.1.
Cdigo 1.1


Si analizamos detenidamente la prueba vemos que ya hemos tomado decisiones
que tienen un impacto muy grande sobre el cdigo que tendremos que escribir (en este
caso la clase carrito). Las primeras decisiones que tomamos al escribir una prueba estn
en la lnea: cmo creamos un objeto carrito? Cul es el nombre de la clase carrito?
Tendr un constructor sin parmetros?
Despus, en la lnea 9, seguimos tomando decisiones, por ejemplo cmo
aadimos elementos al carrito: usaremos una llamada a un mtodo? Cul ser el
nombre de ese mtodo? Qu parmetros tendr y de qu tipo sern?
Por ltimo, en la lnea 11, tomamos decisiones sobre cmo comprobamos que el
carrito ha almacenado correctamente el producto, por ejemplo: qu mtodos
incluimos? Cul es el resultado esperado? Hay que escribir antes una prueba para
dicho mtodo? Con TDD, antes de escribir el cdigo nos centramos cmo queremos
usarlo y nuestras primeras pruebas son requisitos o declaraciones de cmo debe ser
dicho uso.
12

El diario de diseo
El paso previo antes de empezar con un ciclo TDD (que veremos en la prxima seccin)
es decidir sobre qu vamos a trabajar: acceso a datos? validaciones? disear nuevas
clases? implementar lgica de negocio? Mejorar cosas que se nos han quedado
pendientes? Para centrar nuestro trabajo utilizaremos el diario de diseo.
Este diario es la lista de tareas que tenemos pendientes de hacer. Estas tareas
sern, por lo general, funcionalidad o futuras pruebas a escribir. El principal objetivo del
diario de diseo es mantenernos en todo momento con el foco centrado. No te ha
pasado nunca que te has concentrado mucho en escribir una prueba y luego no
recordabas muy bien qu queras implementar? El diario nos ayudar con esto.
Utilizaremos el diario principalmente en dos momentos: el primero ser cuando
ya tengamos una funcionalidad implementada (y gracias a TDD probada) para decidir la
siguiente. El segundo momento ser cuando, durante un ciclo de TDD, se nos ocurra
nueva funcionalidad o escenarios adicionales (por ejemplo: nulos, cadenas o colecciones
vacas, nmeros negativos, etc.) a implementar en nuestro cdigo.
Figura 1.1. Ejemplo de diario de diseo.


En la figura 1.1 tienes un ejemplo de diario de diseo utilizada por Kent Beck en
su libro Test Driven Development: By Example para implementar una clase que
represente una cantidad en un sistema monetario.
Como puedes ver en la figura 1.1, el diario de diseo no es una lista ordenada ni
priorizada. En el momento de elegir una nueva tarea para trabajar, tienes libertad de
elegir aquella que consideres ms importante. Tampoco tienes que limitarte por
posibles dependencias entre tareas, recuerda que las pruebas unitarias rompen las
dependencias por lo que stas no condicionan la eleccin.
13

Veremos ejemplos de diarios de diseo en los ejemplos de los prximos
captulos.
El ciclo de Test-Driven Development
El ciclo de TDD es el conjunto de pasos que seguimos para implementar un nuevo
fragmento de cdigo de produccin. El ciclo de TDD utiliza las pruebas para definir qu
resultado queremos obtener del cdigo que vamos a escribir. Estas pruebas nos ayudan
a tomar las decisiones de diseo de cdigo ms adecuadas. El ciclo completo de TDD se
muestra en la figura 1.1. A continuacin, veremos cada elemento con ms detalle.
Figura 1.2. Ciclo TDD.


El primer paso para empezar con el ciclo TDD consiste en escribir una prueba
que ponga de relieve la funcionalidad que queremos implementar. Esto nos obliga a
definir qu es lo que queremos obtener con el cdigo que vamos a escribir.
A continuacin ejecutamos la prueba y vemos como falla. Si la prueba no falla
estudiamos qu est sucediendo y elegimos otra.
Despus, escribimos el cdigo mnimo (ms corto) para que la prueba pase con
xito. No nos preocupamos de escribirlo bonito, tenemos libertad para tomar atajos.
A continuacin ejecutamos de nuevo todas nuestras pruebas si todo ha ido bien
la prueba que fall antes ahora debera ejecutarse con xito y todas las pruebas que
antes se ejecutaban con xito ahora lo seguirn haciendo. Si no debemos parar e
investigar qu ha sucedido en el paso anterior.
Por ltimo, quitamos los atajos, refactorizamos el cdigo y las pruebas y
ejecutamos las pruebas para asegurar que todo sigue funcionando correctamente y que
14

no hemos introducido ningn error en la refactorizacin. Vers varios ejemplos de
refactorizaciones en los ejemplos de los prximos captulos.
Esto es todo TDD, a partir de aqu ya puedes empezar a aplicarlo. Sin embargo es
interesante que conozcamos un poco ms del por qu hacemos las cosas de esta
manera.
Cdigo mnimo para pasar una prueba
En el ciclo de Test-Driven Development, a la hora de implementar nuestro cdigo y
hacer que la prueba pase, intentamos implementar slo el mnimo cdigo necesario y
tan sencillo como sea posible. A esto lo llamamos avanzar en pequeos pasos, o
babysteps.
Puede ser frustrante tener la implementacin de un mtodo en la cabeza y tener
que parar porque no tenemos todas las pruebas que necesitamos para poder escribirla.
Sin embargo siempre podemos introducir un error en el cdigo sin darnos cuenta, por lo
que es mejor avanzar ms despacio pero ms seguro.
Tambin podemos tener una idea muy buena en la cabeza de cmo tiene que
ser un mtodo o un API pero luego descubrir que no es la manera ms cmoda de
utilizar. Por eso es mejor avanzar ms despacio pero comprobando que nuestro cdigo
es usable.
Aunque la primera vez nos parezca que ese cdigo no tenga valor, s que lo
tiene. Esto lo iremos descubriendo a medida que ganemos experiencia aplicando TDD.
Cuando algo falle nos ser de mucha ayuda acotar el problema en un pequeo
fragmento de cdigo. Adems, el tener mtodos que no tengan demasiadas lneas de
cdigo es una heurstica de buen diseo.
Tambin debemos aplicar babysteps a las pruebas. Las pruebas tambin deben
implementar el mnimo cdigo necesario y cuanto menos mejor. As no gastamos
demasiado tiempo en escribirlas y podemos volver rpidamente a continuar trabajando
en la funcionalidad y, algo tambin muy importante, podemos entender el objetivo de la
prueba rpidamente ojeando el cdigo
Un ltimo motivo para escribir pruebas cortas y el mnimo cdigo posible es no
perder la concentracin sobre lo que estamos trabajando (o no perder el foco). Uno de
los puntos clave de TDD es ejecutar las pruebas muy a menudo. Si nos detenemos
demasiado en una implementacin o intentamos abarcar demasiado probablemente
surjan muchos errores que nos lleven a entrar en un bucle de ejecutar las pruebas, hacer
un pequeo cambio en el cdigo (una condicin de un if, el lmite de un for, etc.), volver
a ejecutar las pruebas, volver a cambiar, etc. Esto es montono, aburrido y hace que nos
olvidemos qu funcionalidad estamos implementando. Veremos muchos ejemplos de
cdigo mnimo a lo largo de este libro.
15

Empezar fallando
Otro aspecto que puede resultar desconcertante en TDD es el ejecutar la prueba en
primer lugar y que falle. Vamos a detenernos en ver los motivos.
Las pruebas tienen que ser simples ya que no escribimos pruebas que prueben
las pruebas. Por ello las pruebas tienen que ser rpidas y pequeas y deben fallar.
Una prueba que funciona nada ms escribirla puede indicar o bien que el cdigo
no funciona como pensamos o bien que ya hace lo que debera. En ambos casos puede
merecer la pena replantear el prximo paso.
Una excepcin a esto son pruebas que son redundantes, es decir, que ya
verifican algn escenario contemplado por otras pruebas. Aunque no parece lgico
aadir pruebas redundantes, pueden ser tiles para documentar bien el cdigo, exponer
escenarios ms complejos o, simplemente, aumentar nuestra sensacin de seguridad y
control a la hora de hacer cambios. Siempre que mantengamos el cdigo de pruebas
actualizado no habr ningn problema al contar con pruebas redundantes. TDD no dice
nada sobre estas pruebas as que puedes aadirlas si lo deseas siempre que te
comprometas a refactorizarlas y a mantenerlas como al resto del cdigo. Vamos a hablar
de esto justo a continuacin.
La importancia de la refactorizacin de todo el cdigo
Refactorizar es cambiar la estructura interna del cdigo para mejorar su calidad, pero sin
cambiar su funcionalidad e interfaces externas. Refactorizar es una tarea que algunos
desarrolladores pueden olvidar presionados por el tiempo. Sin embargo es un punto
vital para que funcione TDD ya que esta buena prctica trabaja de manera incremental.
Veamos las razones.
Un desarrollador no pasa la mayor parte de su tiempo escribiendo cdigo
nuevo, sino que pasa la mayor parte del tiempo diseando cdigo, modificando cdigo
y haciendo que fragmentos de cdigo funcionen bien juntos. Por tanto un desarrollador
tiene que ser capaz de leer y entender el cdigo. Las refactorizaciones nos ayudan a pulir
y mejorar el cdigo para que sea ms sencillo de entender y actualizar. Las pruebas nos
dan la tranquilidad de que el cdigo va a seguir funcionando correctamente. Veamos un
ejemplo, cul de las dos pruebas del cdigo 1.1 se entiende mejor?
En la primera prueba exponemos detalles de bajo nivel que no todos tienen que
conocer, por ejemplo la manera de crear productos. Si repetimos la creacin de
productos en muchas pruebas y luego la creacin de un producto cambia (lo que pasa
con ms frecuencia de lo que uno se imagina) tendremos que arreglar muchas pruebas.
Adems, en esta prueba no nos interesa conocer los detalles del producto.

16

Cdigo 1.1
Prueba A

Prueba B




Otro ejemplo: Por qu el total del carrito debe ser exactamente 118.5? Qu
significa ese nmero? Si vemos la prueba B comprobamos que el total del carrito debe
ser tres veces el precio del producto porque hemos aadido 3 copias.
Si no refactorizamos el cdigo de nuestra aplicacin, cada vez ser ms difcil de
entender, ms difcil de modificar y ms difcil corregir los errores. Eso nos empujar a
buscar alternativas o rodeos que empeorarn la situacin.
Si tampoco refactorizamos el cdigo de pruebas tambin ser ms difcil de
entender y modificar con el paso del tiempo. Cuando una prueba falle nos costar ms
esfuerzo descubrir qu est fallando. Adems cuando el cdigo cambie nos costar ms
esfuerzo cambiar las pruebas y, poco a poco, tenderemos a dejarlas de lado con lo que
nuestro cdigo no tendr la red de proteccin de las pruebas y perderemos la confianza
en l.
17

Cmo continuar aplicando el ciclo TDD
En el ciclo de TDD, primero tenemos que escribir una prueba que falle, pero qu
podemos hacer despus? Existen tres estrategias: fake, triangulacin e implementacin
obvia. No hay ningn orden para aplicar estas estrategias, aunque es muy comn
comenzar fake y luego evolucionar a triangulacin o Implementacin Obvia.
La estrategia fake consiste en simular el resultado o comportamiento esperado
de la forma ms rpida y sencilla posible. Para ello podemos incrustar el valor esperado
en el cuerpo de la funcin. Utilizamos la estrategia fake cuando no tenemos muy claro
cmo implementar un mtodo, o cuando la implementacin es demasiado larga o
compleja para hacerla en un nico paso o cuando queremos priorizar la creacin de
interfaces fciles de usar antes de entrar en los detalles de sus implementaciones.
La estrategia triangulacin consiste en aadir nuevas pruebas a una misma
funcionalidad buscado situaciones no implementadas an. Con esta tcnica podemos
evolucionar los fakes hasta la implementacin en pequeos pasos (o babysteps).
En algunas ocasiones, el cdigo a escribir para superar una prueba sigue un
patrn muy claro y conocido, o bien es lo suficientemente pequeo para poder escribirlo
en poco tiempo. En estos casos se puede implementar el cdigo final directamente. Esta
es la Implementacin Obvia. Algunos ejemplos de implementaciones obvias son recorrer
colecciones, mtodos get/set, etc.
Heursticas TDD
En un desarrollo real no tenemos que obsesionarnos por aplicar TDD al pie de la letra. Al
contrario, lo ms beneficio es adaptar TDD a nuestra manera de trabajar,
complementarlo con otras tcnicas para obtener los mejores resultados.
Sin embargo, si estamos aprendiendo o practicando TDD s es til saber si lo
estamos aplicando bien o no, para no coger ningn mal hbito. A continuacin veremos
algunas ideas para poder autoevaluar si estamos aplicando TDD con xito.
Empecemos repasando las mtricas de cdigo. Algunas mtricas pueden
ayudarnos a detectar si estamos obteniendo buenos resultados o no. A continuacin se
muestra un listado de mtricas fciles de medir.
Tamao de los mtodos (pocas lneas).
Pocos parmetros en los mtodos (no ms de 3).
Clases e interfaces con pocos mtodos.
Alta cohesin y bajo acoplamiento.
Alta cobertura de pruebas (90% o ms).
18

Cantidad de cdigo duplicado.
La cobertura es la cantidad de cdigo fuente que es ejecutado por las pruebas.
El valor del 90% es una referencia y puede variar dependiendo del proyecto / tecnologa.
Algunas herramientas para calcular mtricas y cobertura de cdigo que puedes utilizar
en Java son PMD, CheckStyle, EclEmma o Sonar. PMD incluye, adems, una
herramienta llamada CPD para detectar cdigo duplicado.

Hay que tener cuidado con las mtricas ya que si nos centramos demasiado en ellas
podemos desplazar el foco de nuestro trabajo a conseguir buenos nmeros en vez de
buscar la felicidad de nuestros clientes y usuarios. Por ejemplo podemos tener una alta
cobertura de cdigo o mtodos pequeos y clases poco cohesionadas pero un cdigo
mal diseado, difcil de entender y de modificar.
Ejecutar las pruebas con mucha frecuencia y dejar transcurrir poco tiempo entre
ejecucin y ejecucin del conjunto de pruebas son dos indicadores de que estamos
aplicando TDD adecuadamente, aunque pueden ser ms difciles de medir que las
mtricas anteriores.
Formato del cdigo
A partir de aqu encontrars mucho cdigo fuente. Para ayudarte a interpretar y
entender el cdigo seguimos las siguientes convenciones.
Todo el cdigo est en un cuadro con dos nmeros en formato X.Y, el nmero X
indica el captulo y el nmero Y es nico en cada captulo. Algunas veces, adems, se
aade un encabezado que resume el contenido del cdigo. En cualquier caso, siempre
encontrars una explicacin del cdigo muy cerca de dnde aparezca.
Adems, encontrars cuadros con cdigo de prueba y cuadros con cdigo de
produccin (cdigo que implementa el problema). Puedes distinguirlos fcilmente
porque el cdigo de pruebas tiene un fondo gris.
Tambin encontrars cuadros, tablas y figuras que siguen la misma regla de
numeracin.
Para terminar
Si es la primera vez que te acercas a TDD utiliza todo lo que has visto en este captulo
para hacerte ms fcil aplicarlo. A medida que ganes experiencia probablemente
descubras pequeas variantes, o alternativas, o mejoras para adaptar TDD a tu manera
de trabajar. No dudes en aplicarlas y recuerda que TDD es una buena prctica y el
19

contenido de este captulo solo es una ayuda, nunca una norma de obligado
cumplimiento.
Si ya conocas TDD, compara tu experiencia con todo lo que hemos visto en este
captulo. Seguro que encuentras algo en lo que puedas mejorar.
Ests preparado para empezar a aplicar TDD? Si lo ests ve al prximo captulo
y si no, ve tambin al prximo captulo!


En este libro utilizamos la palabra japonesa kata que significa literalmente forma y
que se utiliza para nombrar a una serie de movimientos utilizada en la prctica de
muchas disciplinas, como el teatro kabuki o las artes marciales En la comunidad global
de TDD este trmino se emplea con mucha frecuencia para designar ejercicios
pensados para mejorar nuestras habilidades programando.

Enlaces y referencias
En este captulo hemos visto, de manera resumida, los conceptos bsicos del desarrollo
dirigido por pruebas y buenas prcticas para aplicarlo con xito. A partir de este
captulo, continuaremos aprendiendo ms sobre TDD mediante ejemplos prcticos. Mi
consejo como autor de este libro es que comiences a aplicar estos conocimientos y a
practicar TDD ahora mismo con los ejemplos que encontradas en los prximos captulos.
Si quieres saber ms de TDD o si durante los ejemplos encuentras alguna laguna
que quieras completar, puedes utilizar las siguientes referencias.
Diseo gil con TDD
http://www.dirigidoportests.com/el-libro
Testing Unitario con Microsoft Fakes
https://vsartesttoolingguide.codeplex.com/downloads/get/720407
Materiales del curso on-line TDD desde Cero
http://www.iwt2.org/web/opencms/IWT2/formacion/catalogo/curso0006.html?locale=es
Blog Aprendiendo TDD
http://aprendiendotdd.wordpress.com/
Blog Hola TDD
http://holatdd.com/
Agradecimientos del captulo
A todos los alumnos de la primera edicin del curso Desarrollo Dirigido por Pruebas
desde Cero porque sus ejercicios, dudas y reflexiones me han ayudado mucho a darle
forma a este captulo.
20



Captulo 2.
Primeros ejemplos de desarrollo
dirigido por pruebas



Antes de empezar con ejemplos de mayor tamao vamos a ver ejemplos pequeos y
sencillos de desarrollo de software dirigido por pruebas para empezar a aplicar todo lo
visto en el captulo anterior. Estos ejemplos servirn para introducir los fundamentos de
TDD que aplicaremos en los ejercicios de los prximos captulos.
Listas palndromas (en Java)
En nuestro primer ejercicio vamos a implementar un mtodo que verifique si una lista es
palndroma, es decir, si el primer elemento coincide con el ltimo, el segundo con el
penltimo, etc. El algoritmo que implementaremos recorrer las dos mitades de la lista
a la vez, la primera mitad de orden creciente y la segunda mitad en orden decreciente,
comparando los elementos de ambas mitades. Podemos empezar definiendo dos casos
a tener en cuanta, cuando la lista tiene nmero de elementos pares, y cuando la lista
tiene nmero de elementos impares, en cuyo caso el elemento central lo ignoramos.
Todo lo anterior junto con ms ideas que se nos ocurran, como por ejemplo
algunos especiales, lo anotamos en el diario de diseo. Este diario se muestra en el
cuadro 2.1.


21

Cuadro 2.1. Diario de diseo inicial para las listas palndromas.
Caso especial: lista vaca no es palndroma
Caso especial. Lista con un nico elemento no es palndroma.
Comprobar listas con nmero de elementos pares.
Comprobar listas con nmeros impares

Para empezar elegimos la entrada de nuestro diario de diseo que se convertir
en nuestra primera prueba. Vamos a comprobar listas de nmeros de elementos pres,
as que escribimos nuestra primera prueba (cdigo 2.1). Esta prueba nos sirve para
definir cmo va a ser la funcin: Qu parmetros necesita? Cmos e llamar? Etc.
Cdigo 2.1
@Test
public void testPalindromaDeEnteros_Par() {
List<Integer> l = Arrays.asList(1,2,2,1);
Boolean b = ListasPalindromas.isPalindroma(l);

assertTrue(b);
}


Vamos a escribir ahora el mnimo cdigo que pasa la prueba (cdigo 2.2). En
concreto aplicamos la estrategia fake vista en el captulo anterior.
Cdigo 2.2
public static <T >boolean isPalindroma(List<T> lista){
return true;
}

La prueba pasa con xito. Vamos a triangular y, para ello, aadimos una segunda
prueba en la que el mtodo detecta que una lista no es palndroma. Una posible prueba
se muestra en el cdigo 2.3.
Cdigo 2.3
@Test
public void testNoPalindromaDeEnteros_Par() {
List<Integer> l = Arrays.asList(1,2,3,1);
Boolean b = ListasPalindromas.isPalindroma(l);

assertFalse(b);
}


A partir de las dos pruebas anteriores vamos a implementar el ncleo de la
comparacin. El cdigo mnimo que hace que ambas pruebas se ejecuten con xito se
muestra en el cdigo 2.4.

22

Cdigo 2.4.
public static <T >boolean isPalindroma(List<T> lista){
for(int i = 0; i < lista.size() / 2; i++){
if (lista.get(i) != lista.get(lista.size()-1 - i)){
return false;
}
}
return true;
}



Antes de continuar, vamos a refactorizar para que el algoritmo sea ms sencillo
de entender. Compara el cdigo del cuadro 2.4 con el cdigo del cuadro 2.5. Es ms
sencillo de entender? Qu cambios haras para mejorar la legibilidad? En el primer
captulo repasamos el por qu es muy importante que el cdigo sea fcil de leer cuando
aplicamos TDD.
Cdigo 2.5. Cdigo refactorizado.
public static <T >boolean isPalindroma(List<T> lista){
int mitad = lista.size() / 2;
int ultimoelemento = lista.size()-1;

for(int i = 0; i < mitad; i++){
if (lista.get(i) != lista.get(ultimoelemento - i)){
return false;
}
}
return true;
}


La solucin anterior funciona con listas de elementos pares. Nuestro siguiente
paso ser aadir pruebas para que el cdigo trabaje tambin con listas con un nmero
impar de elementos. Aadimos dos pruebas con listas impares (cdigo 2.6 y cdigo 2.7).
Cdigo 2.6. Ejemplo de lista palndroma con tamao impar.
@Test
public void testPalindromaDeEnteros_Impar() {
List<Integer> l = Arrays.asList(1,2,3,2,1);
Boolean b = ListasPalindromas.isPalindroma(l);

assertTrue(b);
}


Cdigo 2.7. Ejemplo de lista no palndroma con tamao impar..
@Test
public void testNoPalindromaDeEnteros_ImPar() {
List<Integer> l = Arrays.asList(1,2,3,3,1);
Boolean b = ListasPalindromas.isPalindroma(l);
23


assertFalse(b);
}


El conjunto de pruebas sigue funcionando correctamente, incluidas las pruebas
de los cuadros 2.6 y 2.7. Busquemos ahora pruebas que nos hagan escribir nuevo cdigo
de produccin. Por ejemplo podemos implementar la gestin de una lista vaca. Para
ello escribimos la prueba del cdigo 2.8.
Cdigo 2.8.
@Test
public void testListaVacia() {
List<Integer> l = Arrays.asList();
Boolean b = ListasPalindromas.isPalindroma(l);

assertFalse(b);
}


Y, ahora s, la nueva prueba falla y podemos escribir cdigo adicional. El
resultado se muestra a continuacin.
Cdigo 2.9.
public static <T >boolean isPalindroma(List<T> lista) {
if (lista.size() == 0 ) {
return false;
}
int mitad = lista.size() / 2;
int ultimoelemento = lista.size()-1;

for(int i = 0; i < mitad; i++) {
if (lista.get(i) != lista.get(ultimoelemento - i)) {
return false;
}
}
return true;
}

Sin embargo si la lista tiene un nico elemento tampoco ser posible comprobar
si es o no palndroma, por lo que aadimos una nueva prueba. En este caso tomo la
decisin de que una lista con un nico elemento no es palndroma.



24

Cdigo 2.10.
@Test
public void testListaUnElemento() {
List<Integer> l = Arrays.asList(1);
Boolean b = ListasPalindromas.isPalindroma(l);

assertFalse(b);
}


A continuacin, modificamos el cdigo para que todas las pruebas que tenemos
hasta ahora se ejecuten con xito.
Cdigo 2.11.
public static <T >boolean isPalindroma(List<T> lista) {
if (lista.size() < 2 ) {
return false;
}

int mitad = lista.size() / 2;
int ultimoelemento = lista.size()-1;

for(int i = 0; i < mitad; i++) {
if (lista.get(i) != lista.get(ultimoelemento - i)) {
return false;
}
}
return true;
}


Y ya est terminado. Despus de revisar el diario de diseo vemos que hemos
acabado con todas las tareas por lo que podemos pasar al siguiente ejercicio.
Tipo de dato lista (en Python)
En esta seccin vamos a ver un ejemplo de cmo utilizar las pruebas como requisitos
con una clase muy sencilla que incluya alguna de las operaciones del tipo de dato lista.
No vamos a empezar pensando en qu mtodos tiene que tener la lista ni en los detalles
de su implementacin sino en ejemplos de cmo queremos utilizarla y los
documentamos en nuestro diario de diseo:
Cuadro 2.2. Diario de diseo.
El nmero de elementos de una lista vaca es cero.
Al aadir un elemento a una lista, el nmero de elementos se incrementa en
uno.
En una lista vaca, a la que se aaden, los elementos A,B, y C, el elemento A
tiene asociado el ndice 0, el elemento B tiene asociado el ndice 1 y el
elemento C tiene asoacido el ndice 2

25

Podemos codificar estos ejemplos como pruebas en Python para tomar
decisiones sobre cmo vamos a utilizar la clase lista y para ir verificando que todo
funciona correctamente a medida que la programamos. Para ello utilizamos el mdulo
unittest que ya viene incluido en la distribucin base de Python. Una posible
implementacin de los ejemplos anteriores se muestra a continuacin.

Cdigo 2.12. Pruebas en Python
class TestsCasesForMyList(unittest.TestCase):
def test_GivenANewListThenItHasZeroElements(self):
lista = MyList()
self.assertEqual(0, lista.size())

def test_GivenANewListWhenIAddAnElementThenItHasOneElement(self):
lista = MyList()
lista.add("A")
self.assertEqual(1, lista.size())

def test_GivenANewListWhenIAdd_A_B_C_Then_A_HasIndex_0_B _1_and_C_2
(self):
lista = MyList("A", "B", "C")
self.assertEqual("A", lista.get(0))
self.assertEqual("B", lista.get(1))
self.assertEqual("C", lista.get(2))


Fijmonos en las decisiones de diseo y requisitos que hemos establecido puede
que sin darnos cuenta. Por ejemplo, si nos fijamos en la tercera prueba vemos que,
aunque nadie lo haba mencionado, nos resulta muy cmodo indicar un grupo de
elementos al momento de crear la lista, en vez de aadirlo uno a uno. Si no hubiramos
puesto este ejemplo puede que no lo hubiramos descubierto.
La implementacin de las pruebas anteriores, siguiendo la regla del mnimo
cdigo se muestra a continuacin.
Cdigo 2.13. Implementacin de la lista en Python
class MyList:
def __init__(self, *val):
self.elements = 0

def size(self):
return self.elements

def add(self, elem):
self.elements += 1

def get(self, index):
if index == 0:
return "A"
if index == 1:
return "B"
return "C"


26

Cul sera tu siguiente paso para continuar aplicando TDD a partir de aqu?
Tienes la solucin al final de este captulo.
Para reflexionar
Vamos a comentar algunas decisiones que hemos tomado en los ejercicios anteriores.
Empezamos repasando el mnimo cdigo de produccin necesario para que una prueba
funcione. Aplicando este principio hemos escrito el cdigo 2.14 para implementar la
primera prueba del ejercicio de listas palndroma.
Cdigo 2.14
public static <T >boolean isPalindroma(List<T> lista){
return true;
}

No es este cdigo demasiado trivial para aportar algo? No, este cdigo es
valioso, porque, en primer lugar me permite pasar la prueba de fallo a xito y me da
seguridad para seguir avanzando. En segundo lugar sirve como comprobante de que la
prueba funciona. En tercer y ltimo lugar me permite usar mi propio cdigo y darme
cuenta si el nombre del mtodo, parmetros, tipo devuelto, etc. son la mejor eleccin.
Como vimos en el captulo anterior, a partir de este cdigo podemos tomar tres
caminos: cambiar el fake por el cdigo autntico, escribir una nueva prueba que me
obligue a cambiar el cdigo para que las pruebas pasen, o escribir una prueba que me
lleve a implementar otro mtodo.
La tercera opcin no tiene sentido aqu ya que no estamos construyendo un API
ni similares, pero podra tener sentido si quisiramos modelar una interfaz externa
cmoda de usar antes de entrar en la implementacin.
La primera opcin, personalmente, no convence en este caso. Creo que el
cdigo real de produccin es muy complejo para implementarlo en un nico pequeo
paso (babystep). Por este motivo eleg la segunda opcin y aad una segunda prueba
como vimos al principio del captulo. Esta es una opcin personal que dicta la prctica,
cualquier otra opcin probablemente sera igual de interesante ya que conduce a
aumentar la experiencia y, por tanto, la capacidad de reflexionar y aprender.
Recuerda tambin que buscamos avanzar en la implementacin con pequeos
pasos. Un error comn al principio es escribir una implementacin obvia como la del
cdigo 2.14 y triangular para intentar implementar 30 o 40 lneas cdigo de golpe. Busca
que la triangulacin te lleve a implementar solo unas pocas lneas de cdigo. Aunque
pienses que es una implementacin obvia, intenta no escribir demasiado cdigo de
produccin, recuerda que tambin tenemos que ejecutar las pruebas muy a menudo
27

Otro aspecto interesante para reflexionar es qu hacer con pruebas
innecesarias. Hemos visto en el primer ejemplo que las pruebas de los cuadros 2.6 y 2.7
no nos hacan escribir nuevo cdigo, es decir, funcionan con el cdigo actual. Por qu
escribirlas? Desde el punto de vista de TDD estas pruebas sobraran y no deberamos
haberlas escrito.
No estamos obligados aplicar TDD durante todo el desarrollo y podemos escribir
pruebas por otros motivos adems de para escribir nuevo cdigo. Podemos utilizar las
pruebas para documentar adecuadamente escenarios adicionales. Observa que, aunque
el cdigo tal y como lo tenemos se comporta correctamente, puede no ser obvio cual es
el resultado esperado para las listas impares. Las pruebas de los cuadros 2.6 y 2.7 hacen
ms claro ese resultado.
En general, salimos del ciclo de TDD cuando queremos aadir pruebas
adicionales, cuando no seguimos escribiendo nuevo cdigo, por ejemplo para dedicar
ms tiempo a refactorizar, o cuando consideramos que TDD no nos aporta ningn valor
en el cdigo a escribir. Nunca, nunca, nunca (s, tres veces) debes abandonar TDD
porque una prueba te resulte difcil. Justo al contrario, si una prueba te resulta difcil e
escribir TDD est haciendo bien su trabajo avisndonos de que debemos volver a pensar
en el diseo del cdigo.
Solucin al ejercicio planteado
Una posible solucin para continuar con el ejercicio de la lista en Python est en el
cuadro 2.15. Completar.
Cdigo 2.15. (copia del 2.13).
class MyList:
def __init__(self, *val):
self.elements = 0

def size(self):
return self.elements

def add(self, elem):
self.elements += 1

def get(self, index):
if index == 0:
return "A"
if index == 1:
return "B"
return "C"

El mtodo get es un fake, hemos incrustado los valores que tiene que devolver.
Sin embargo es difcil hacer evolucionar esta implementacin ya que no hemos definido
una prueba que nos lleve a incluir en MyList una manera de guardar los elementos.

28

Agradecimientos del captulo
A la comunidad Solveet (www.solveet.com) y al excelente trabajo que hace
Rubn Bernrdez con su mantenimiento.
A Juan Luis Cano y al blog de Pybonacci (pybonacci.wordpress.com) por su
apoyo a la hora de escribir los ejemplos en Python.



29



Captulo 3.
Criba de Eratstenes en Java



En este captulo realizaremos un ejemplo de mayor tamao que implementaremos
tambin aplicando el ciclo de desarrollo dirigido por pruebas que vimos en el primer
captulo.

El problema
Implementar, aplicando TDD, una versin muy sencilla (y poco optimizada) del algoritmo
de la Criba de Eratstenes para calcular la lista de los nmeros primos desde 2 hasta un
nmero n indicado. El algoritmo de la criba de Eratstenes se describe a continuacin.
1. Se crea una lista con los nmeros desde 2 hasta n.
2. Se elige el siguiente nmero x.
3. Se marcan todos los mltiplos de dicho nmero (x*2, x*3, etc.).
4. Se repite desde el paso 2 mientras queden nmeros.
Cuando se ha terminado con todos los nmeros aquellos que queden sin marcar
son primos. Veamos las primeras evoluciones aplicando TDD.
Empezamos por un mal camino
Para empezar a aplicar TDD escribimos nuestra primera prueba. Esta prueba la puedes
ver en el cdigo 3.1. Una vez que probamos que falla (aunque Java ni siquiera permite
ejecutarla) la implementamos con el cdigo mnimo posible, con un fake, que tambin
se muestra en el cdigo 3.1.
30

Cdigo 3.1
Prueba
public class TestCribaEratostenes {
@Test
public void testCalculaConValorInicialUno() {
List<Integer> l = CribaDeEratstenes.Calcula(1);
assertTrue(l.isEmpty());
}
}

Implementacin
static class CribaDeEratstenes {
public static List<Integer> Calcula(int i) {
return new ArrayList<Integer>();
}
}


En este caso hemos empezado definiendo qu sucede cuando intentamos
calcular los nmeros primos hasta el 1. Buscamos ahora una nueva prueba que nos haga
evolucionar mediante triangulacin, por ejemplo buscando los primos del nmero 2,
cuyo resultado esperado debe ser el propio nmero 2. Aplicamos un nuevo ciclo TDD
con el par prueba-cdigo de produccin del cdigo 3.2.

Cdigo 3.2
Prueba
@Test
public void testCalculaConValorInicialDos() {
List<Integer> l = CribaDeEratstenes.Calcula(2);
assertEquals(1, l.size());
assertEquals(new Integer(2), l.get(0));
}

Implementacin
public static List<Integer> Calcula(int i) {
List<Integer> l = new ArrayList<Integer>();

if (i >= 2)
l.add(2);

return l;
}



La prueba del cdigo 3.2 verifica que el resultado es un nico nmero y que
dicho nmero es el 2, que es el resultado esperado al calcular los primos de 2. Esta
prueba falla, ya que el cdigo de produccin siempre devuelve una lista vaca. A
continuacin escribimos el mnimo cdigo para superar esta y todas las dems pruebas
que ya tenemos escritas (cdigo 3.2). Al final de la traza veremos una manera ms
cmoda de escribir este tipo de asserts utilizando las libreras del propio Java.
Vamos a continuar triangulando para aadir ms cdigo al mtodo. La siguiente
prueba verifica los resultados de calcular los primos de 3, para lo cual la lista resultante
debe tener un nuevo elemento (cdigo 3.3).

31

Cdigo 3.3
Prueba
@Test
public void testCalculaConValorInicialTres() {
List<Integer> l = CribaDeEratstenes.Calcula(3);

assertEquals(2, l.size());
assertEquals(new Integer(2), l.get(0));
assertEquals(new Integer(3), l.get(1));
}


Implementacin
public static List<Integer> Calcula(int i) {
List<Integer> l = new ArrayList<Integer>();

if (i >= 2) {
l.add(2);
if ( i >= 3) {
l.add(3);
}

return l;
}

En este nuevo paso puedes ver que hemos entrado en un ciclo que no nos
aporta nada. Si ahora hiciramos una prueba con el valor 5, el mnimo cdigo sera
aadir el valor 5 a la lista devuelta. Pero en este caso no estamos calculando nada.
Este ciclo se puede romper haciendo una refactorizacin. Con ella, en vez aadir
uno a uno los valores esperados, los calculamos. Para hacer esta refactorizacin
Tendramos que dar un paso muy grande con muchos cambios que pueden salir mal
para implementar el cdigo del algoritmo. No estaramos sacndole partido a TDD.
Llegados a este punto ya nos damos cuenta de que los casos de prueba no nos
ayudan a evolucionar el cdigo. Una refactorizacin nos llevara a implementar todo el
mtodo de una vez, sin aplicar TDD ni avanzar en pequeos pasos (babysteps). Hemos
encontrado un mal camino de aplicar TDD, es decir, estamos trabajando de una manera
que una manera que nos no lleva a ningn buen resultado y que nos empuja a probar
otras maneras distintas de trabar.
Vamos a cambiar la manera de aplicar TDD. No tires el cdigo que hemos escrito
hasta ahora. Podremos aprovecharlo ms adelante.
Un nuevo comienzo
El primer paso del nuevo comienzo va a ser redactar el diario de diseo (cuadro 3.1). En
este ejemplo el diario es los pasos del algoritmo.
3.1. Diario de diseo
Construir una lista con los nmeros desde 2 hasta n en el que ningn elemento
est marcado.
Marcar los mltiplos de cada nmero x de la lista (x*2, x*3, etc.).
Construir una lista con todos los nmeros no marcados (la lista de nmeros
primos).

32


Para avanzar pasos ms diminutos, cada paso del algoritmo lo implementaremos
en un mtodo con sus propias pruebas. Aunque dichos mtodos deberan ser privados,
para probarlos con comodidad los pondremos con el mbito de visibilidad menos
restrictivo.
El primer paso que vamos a abordar es crear una matriz de booleanos para
indicar qu nmeros estn marcados y cules no. Escribimos una prueba y, despus
haremos una implementacin obvia (cdigo 3.4).

Cdigo 3.4
Prueba
@Test
public void testCreaListaDeNumerosSinMarcar() {
int tope = 4;
List<Boolean> booleanos =
CribaDeEratstenes.CreaListaDeNumerosSinMarcar(tope);

assertEquals((tope+1), booleanos.size());
for (Boolean b: booleanos) {
assertFalse(b);
}
}

Implementacin

public static List<Boolean> CreaListaDeNumerosSinMarcar(int i) {
List<Boolean> lb = new ArrayList<Boolean>();
for (int c=0; c<=i; c++)
lb.add(false);
return lb;
}



En la implementacin obvia del cdigo 3.4, se ha incrementado el tope en 1 ya
que para que el nmero 4 aparezca en la lista de marcados, es necesario que la lista
tenga 5 elementos (del 0 al 4). Ignoraremos las posiciones 0 y 1 que siempre sern false,
ya que no intervienen en el algoritmo.
No vamos a tener en cuenta la posibilidad de recibir como parmetro un valor
inferior a dos, ya que los valores incorrectos sern filtrados por el mtodo que llamar al
mtodo CreaListaDeNumerosSinMarcar. Aun as, no es una mala idea aadirlo, lo cul te
propongo que ejercicio adicional.
El primer paso de nuestro diario ya est implementado (cuadro 3.2) y lo
tachamos.
3.2. Diario de diseo actualizado.
Construir una lista con los nmeros desde 2 hasta n en el que ningn elemento
est marcado.
Marcar los mltiplos de cada nmero x de la lista (x*2, x*3, etc.).
Construir una lista con todos los nmeros no marcados (la lista de nmeros
primos).
33

Continuamos con la funcionalidad para marcar todos los mltiplos de un nmero
dado. Antes de empezar la implementacin vamos a reflexionar qu significa esta
funcionalidad diseando algunos ejemplos como los que se muestran a continuacin
o En el clculo de los nmeros primos hasta el nmero 2, no queda
marcado ningn nmero.
o En el clculo de los nmeros primos hasta el nmero 3, no queda
marcado ningn nmero.
o En el clculo de los nmeros primos hasta el nmero 4, queda marcado
el nmero 4.
o En el clculo de los nmeros primos hasta el nmero 5, queda marcado
el nmero 4.
Con los ejemplos anteriores ya tenemos ms claro la funcionalidad que vamos a
implementar. Adems vamos a utilizar los ejemplos que consideremos interesantes
como casos de prueba. Vamos a empezar escribiendo una prueba que marque los
valores para calcular los primos de 4, ya que es el primer nmero en el que hay un
cambio en la lista de nmeros marcados (cdigo 3.5). Si usamos el 2 o el 3, al no
marcarse ningn nmero, no habra sido necesario escribir cdigo, por lo que no son
valores de prueba adecuados para hacer un ciclo de TDD.
Cdigo 3.5

@Test
public void testMarcarMultiplosHasta4() {
List<Boolean> booleanos =
CribaDeEratstenes.CreaListaDeNumerosSinMarcar(4);

CribaDeEratstenes.MarcarMultiplos(booleanos);

assertFalse(booleanos.get(2));
assertFalse(booleanos.get(3));
assertTrue(booleanos.get(4));
}


La implementacin obvia que pasa la prueba anterior se muestra en el cdigo
3.6.

Cdigo 3.6

// Cdigo
public static void MarcarMultiplos(List<Boolean> l) {
for (int num = 2; num < l.size(); num++) {
for (int mul = (num*2); mul < l.size(); mul += num) {
l.set(mul, true);
}
}
}


34

La prueba funciona. Ya tenemos implementada otra caracterstica del diario de
diseo y actualizamos el cuadro 3.3.

3.3. Diario de diseo actualizado.
Construir una lista con los nmeros desde 2 hasta n en el que ningn elemento
est marcado.
Marcar los mltiplos de cada nmero x de la lista (x*2, x*3, etc.).
Construir una lista con todos los nmeros no marcados (la lista de nmeros
primos).

La siguiente caracterstica a implementar ser la creacin de la lista de nmeros
primos, es decir, aquellos que no han sido marcados. Una prueba de esta funcionalidad
est en el cdigo 3.7.

Cdigo 3.7

@Test
public void testCrearListaDePrimosHasta4() {
List<Boolean> l =
CribaDeEratstenes.CreaListaDeNumerosSinMarcar(4);
CribaDeEratstenes.MarcarMultiplos(l);

List<Integer> primos =
CribaDeEratstenes.CreaListaDePrimos(l);

assertEquals(2, primos.size());
assertEquals(new Integer(2), primos.get(0));
assertEquals(new Integer(3), primos.get(1));
}



La implementacin obvia que pasa la prueba anterior con xito est en el cdigo
3.8.

Cdigo 3.8

public static List<Integer> CreaListaDePrimos(List<Boolean> lb) {
List<Integer> l = new ArrayList<Integer>();
for (int c = 2; c < lb.size();c++) {
if (!lb.get(c)) {
l.add(c);
}
}
return l;
}



Con este cdigo terminamos los pasos del algoritmo. Veamos cmo ponerlo
todo junto en la siguiente seccin.
35

Integrando
Los pasos del algoritmo ya estn implementados y probados. Ahora es el momento de
dichos pasos en un mtodo que calcule la criba de Eratstenes.
3.1. Diario de diseo
Construir una lista con los nmeros desde 2 hasta n en el que ningn elemento
est marcado.
Marcar los mltiplos de cada nmero x de la lista (x*2, x*3, etc.).
Construir una lista con todos los nmeros no marcados (la lista de nmeros
primos).
Crear el mtodo principal que llame a los tres mtodos auxiliares.

Vamos a reutilizar las primeras pruebas que escribimos al principio de este
captulo (cdigo 3.1, 3.2 y 3.3), por lo que ya contamos con un conjunto de pruebas para
implementar el mtodo Calcula. La implementacin de este mtodo (implementacin
obvia) est en el cdigo 3.9.
Cdigo 3.9,

public static List<Integer> Calcula(int i) {
List<Boolean> lb = CreaListaDeNumerosSinMarcar(i);
MarcarMultiplos(lb);
return CreaListaDePrimos(lb);
}


Las pruebas siguen funcionando por lo que ya podemos dar por terminada la
implementacin. Sin embargo podemos aadir algunas pruebas ms por ejemplo la
prueba 3.10.
Cdigo 3.10.

@Test
public void testGeneraPrimosHastaDoce() {
List<Integer> l = CribaDeEratstenes.Calcula(12);
Assert.assertEquals(l, Arrays.asList(2, 3, 5, 7, 11));
}


La prueba anterior calcula todos los nmeros primos hasta el 12 (inclusive) y,
adems, utiliza Arrays.asList para crear la lista de nmeros esperada lo que facilita la
comprobacin final. Tachamos el ltimo elemento de nuestro diario de diseo, lo que
significa que ya hemos terminado.
36

Conclusiones
Una limitacin que se ha visto en este ejercicio es cmo esconder los mtodos auxiliares
para que solo puedan llamarse desde el mtodo principal, pero, a la vez, permitir que se
prueben de manera independiente. Todo lo que queramos probar eran mtodos
privados, salvo un mtodo director encargado de llamar a los dems (mtodo Calcula)
que es pblico.
Es posible probar mtodos privados en Java, por ejemplo invocarlo mediante
introspeccin o crear una clase hija que defina mtodos pblicos que invoquen a estos
elementos privados. Estas dos alternativas requieren trabajo adicional y hacen a las
pruebas ms frgiles. Como no existe la solucin perfecta en este caso, como decisin
personal, he optado por abrir la visibilidad de los mtodos y he documentado que
dichos mtodos son de uso interno. Con ello he hecho que las pruebas sean lo ms
sencilla posibles.
En ningn momento de este ejemplo hemos refactorizado el cdigo, porque no
hemos visto que fuera necesario, pero esto es muy poco frecuente.
En este ejemplo, hemos implementado el diario de diseo siguiendo los mismos
pasos que emplea la Criba de Eratstenes, pero recuerda que no hay limitacin a la hora
de elegir la siguiente caracterstica a implementar.
Para reflexionar.
Hemos empezado el ejercicio tomando un camino que, con el tiempo, hemos
descubierto que no era el correcto. Los malos olores que hemos encontrado han sido
realizar varios fakes seguidos y no refactorizar.
Es difcil aplicar TDD para implementar mtodos largos porque, como hemos
visto al principio, es difcil ir aadiendo fragmentos de cdigo a un mtodo a medida que
aadimos ms pruebas. En cambio, TDD funciona muy bien descomponiendo el
problema en pasos pequeos y encapsulndolos en mtodos. El diario de diseo
tambin nos ayuda a ello cmo has visto a lo largo de este captulo.
Aunque hayamos empezando por un camino incorrecto no ha sido una prdida
de tiempo. Hemos descubierto una manera de trabajar que no nos funcionaba y que nos
ha ayudado a buscar otro camino distinto y hemos aprovechado las primeras pruebas.
Es muy comn aplicando TDD descubrir que nuestros primeros pasos no van por buen
camino. TDD no nos indica el camino correcto, pero nos ayuda a detectar rpidamente
los caminos incorrectos.
En el ejemplo anterior se dan algunas indicaciones de ejercicios adicionales para
realizar. A continuacin te proponemos uno ms. Es posible optimizar el algoritmo
parando cuando ya no queden ms nmeros por marcar. Intenta aadir esta mejora al
37

cdigo que se ha visto en este captulo. Tendra sentido aplicar TDD para implementar
esta mejora?. Tambin es posible desarrollar nuevas implementaciones utilizando
estructuras ms eficientes, como Bitset, o las nuevas funciones lambda y los streams de
Java 8.



38



Captulo 4.
Dos soluciones para el Mancala




En este captulo vamos a resolver la kata del juego Mancala. Uno de los primeros
problemas que tendremos que afrontar, antes de implementar la lgica de negocio, es
cmo modelar la informacin y qu interfaz externa es la ms cmoda para interactuar
con el Mancala.
Este captulo tiene una organizacin distinta a los captulos anteriores. Despus
de exponer el problema que vamos a abordar, veremos dos soluciones. En la primera
aplicaremos TDD pero de una manera descuidada. En la segunda aplicaremos TDD
mucho mejor y as, t, lector, podrs comparar ambas estrategias y sacar tus propias
conclusiones.
El problema
El Mancala (o Awale o Awir, etc.) es la familia de juegos ms antigua que se conoce. Sus
doscientas variantes se extendieron por frica y parte de Asia. En esta kata vamos a
implementar el movimiento de una de estas variantes.
El tablero de juego consta de 6 pozos para cada jugador y dos chozas (una para
cada jugador). Cada pozo tendr cuatro semillas y las chozas estarn vacas.


39

Imagen 1. Tablero de un Mancala clsico


En cada turno, el jugador selecciona uno de sus 6 pozos (nunca la choza), saca
todas las semillas de l y las planta una a una en los pozos consecutivos siguiendo el
sentido contrario de las agujas del reloj. No se puede seleccionar un pozo si no hay
semillas en l.
En la siguiente imagen acaba de empezar la partida y el jugador A ha
seleccionado su pozo 3, por lo que ha puesto una semilla en los pozos 4, 5, 6 y en su
choza.
Imagen 2. Distribucin de los pozos


Con todo lo anterior la kata ya tiene una dificultad elevada, pero si an quieres
implementar ms funcionalidad, aade las siguientes caractersticas.
Si se da una vuelta completa, no se planta ninguna semilla en el pozo original.
Si la ltima semilla la plantas en un pozo vaco de tu propiedad, coge todas las
semillas del pozo de enfrente y gurdalas en tu choza.
Cuando le toque mover a un jugador y no tenga semillas en sus pozos la partida
termina. El jugador contrario guarda todas sus semillas en su choza y el ganador
es el jugador con mayor nmero de semillas.


40


A continuacin, resolveremos este problema aplicando TDD pero no lo haremos bien. No
tomes las siguientes secciones como ejemplo a seguir porque son justo lo contrario,
cosas que deberas evitar cuando apliques TDD.
Cuando leas esta primera parte intenta buscar fallos en la manera de aplicar TDD. Al
final de esta primera encontrars una retrospectiva dnde analizaremos estos fallos.

Cada semilla en su pozo
Ya tenemos claro el enunciado, as que no perdamos el tiempo y vamos con nuestra
primera prueba. De golpe se nos ocurre representar los pozos y chozas con una clase
que gestionen las semillas que contienen, as el cdigo ser ms sencillo de escribir. Para
poder crear esta clase necesitamos una prueba, as que vamos por ello (cdigo 4.1).
Cdigo 4.1.
public class TestCell {
int seeds = 4;

@Test
public void testIndicateTheNumberOfSeedsWhenCreatingACell() {
Cell c = new Cell(seeds);

assertEquals(seeds, c.getSeeds());
}
}

Y a continuacin, en el cdigo 4.2, tenemos nuestra esperada, y esperemos que
til, clase Cell.
Cdigo 4.2.
public class Cell {

int seeds;

public Cell(int seeds) {
this.seeds = seeds;
}

public int getSeeds() {
return seeds;
}}

El cdigo es lo suficientemente sencillo y rpido para considerarlo un babystep y
escribirlo de una nica vez. De hecho la mayora del cdigo del cuadro 4.2 puede
generarlo Eclipse (u otros IDEs) automticamente.
Ahora que ya tenemos las celdas para guardar semillas vamos a utilizarlas para
crear el tablero. Como el tablero tiene una configuracin inicial definida (4 semillas en
cada pozo y las chozas vacas) vamos a escribir una prueba que recoja todo lo anterior y
que nos empuje a crearlo (cdigo 4.3).
41

Cdigo 4.3.
public class TestBoard {

@Test
public void testNewBoardHas12CellsWith4SeedsEach() {
Board b = new Board();
int cellCount = 0;

for (Cell c: b.getCells()) {
cellCount++;
assertEquals(c.getSeeds(), 4);
}

assertEquals(12, cellCount);
}
}

Con el cdigo 4.3 comprobamos que una instancia de Board tenga 12 pozos
(objetos de la clase Cell) cada una con 4 semillas. Ahora implementamos la clase Board
para que la prueba anterior pase con xito, pero no nos vamos a parar a ver ese cdigo,
sino que vamos a ir a la siguiente prueba.
En la siguiente prueba, cdigo 4.4, comprobamos que el tablero tambin tiene
dos chozas con cero semillas.
Cdigo 4.4
@Test
public void testNewBoardHas2HousesWithoutSeeds() {
Board b = new Board();

for (Cell c: b.getHouses()) {
assertEquals(c.getSeeds(), 0);
}

assertEquals(2, b.getHouses().size());
}

Ahora s, veamos, en el cdigo 4.5, cul es el mnimo cdigo de la clase Board
que permite que las dos pruebas anteriores pasen.
Cdigo 4.5
public class Board {

List<Cell> cells;
List<Cell> houses;

public Board() {
cells = new ArrayList<Cell>();
for (int i = 0; i < 12; i++) {
cells.add(new Cell(4));
}
houses = new ArrayList<Cell>();
for (int i = 0; i < 2; i++) {
houses.add(new Cell(0));
}
}

public List<Cell> getCells() {
return cells;
42

}

public List<Cell> getHouses() {
return houses;
}

}

Las pruebas de los cdigos 4.3 y 4.4 son muy similares. Adems, en el
constructor de la clase Board estamos repitiendo el mismo fragmento de cdigo 2 veces.
Todo lo anterior son malos olores que nos animan a refactorizar. El cdigo refactorizado
de las pruebas se muestra en el cdigo 4.6. Hemos creado un mtodo assert local
assertAllCellsHasSameSeeds para evitar el cdigo duplicado.
Cdigo 4.6
public class TestBoard {

Board b;

@Before
public void setUp() {
b = new Board();
}

@Test
public void testNewBoardHas12CellsWith4SeedsEach() {
assertAllCellsHasSameSeeds(b.getCells(), 4);
assertEquals(12, b.getCells().size());
}

@Test
public void testNewBoardHas2HousesWithoutSeeds() {
assertAllCellsHasSameSeeds(b.getHouses(), 0);
assertEquals(2, b.getHouses().size());
}


void assertAllCellsHasSameSeeds(List<Cell> l, int seeds) {
for (Cell c: l) {
assertEquals(c.getSeeds(), seeds);
}
}
}

Y el cdigo refactorizado de la clase Board se muestra en el cdigo 4.7. En esta
refactorizacin hemos movido el cdigo duplicado al mtodo privado createListOfCells.
Cdigo 4.7
public class Board {

List<Cell> cells;
List<Cell> houses;

private List<Cell> createListOfCells(int cellsNumber, int value) {
List<Cell> cells = new ArrayList<Cell>();
for (int i = 0; i < cellsNumber; i++) {
cells.add(new Cell(value));
}
return cells;
}
43


public Board() {
cells = this.createListOfCells(12, 4);
houses = this.createListOfCells(2, 0);
}

public List<Cell> getCells() {
return cells;
}

public List<Cell> getHouses() {
return houses;
}
}

Ahora que ya tenemos nuestro tablero, chozas y pozos, vamos a dar el siguiente
paso y vamos a implementar la operacin de sembrar.
Sembrar para el futuro
Cuando sembramos, desencadenamos varios efectos. Uno de ellos es que el pozo
original queda sin semillas. Vamos a escribir una prueba que ponga esto de manifiesto.
Cdigo 4.8
@Test
public void testSeeding4SeedsFromCell0_CellHas0Seeds() {
b.seed(0);

assertEquals(0, b.getCells().get(0).getSeeds());
}

En este caso, estamos identificando los pozos por su ndice. Uno de los
jugadores tendr el pozo de 0 a 5 y el otro de 6 a 11. Para implementar esta prueba
tenemos que aadir el mtodo seed a la clase Board (cdigo 4.9).
Cdigo 4.9. Clase Board

public void seed(int i) {
this.cells.get(i).removeSeeds();
}


Para que la prueba funcione, tenemos que implementar el mtodo removeSeeds
en Cell para poder quitar las semillas de ese pozo. Su implementacin se muestra en el
cdigo 4.10
Cdigo 4.10. Clase Cell

public void removeSeeds() {
seeds = 0;
}


44

La prueba original (cdigo 4.8) nos ha empujado a implementar tambin un mtodo en
la clase Cell aunque no hemos escrito ninguna prueba para este mtodo. La prueba del
mtodo removeSeed es la propia prueba del cdigo 4.8 ya que si el mtodo removeSeed
de Cell no estuviera bien implementado, esta prueba fallara.
Nuestro siguiente paso ser aadir las semillas que hemos quitado al resto de
los pozos.
Haced sitio
Otro cambio en el tablero, cuando sembramos es que los pozos (y chozas) adyacentes
ganan una semilla. Vamos a escribir una prueba que siembre las semillas del pozo 0 y
verifique que el nmero de semillas de los pozos 1, 2, 3 y 4 se ha incrementado en uno.
Cdigo 4.11. Clase Cell
@Test
public void
testSeeding4SeedsFromCell0_Cells_1_2_3_4_HasOneMoreSeeds() {
b.seed(0);

assertEquals(seeds+1, b.getCells().get(1).getSeeds());
assertEquals(seeds+1, b.getCells().get(2).getSeeds());
assertEquals(seeds+1, b.getCells().get(3).getSeeds());
assertEquals(seeds+1, b.getCells().get(4).getSeeds());

}

Con la prueba del cdigo 4.11, triangulamos el mtodo seed. Tenemos que
aadir cdigo adicional al mtodo seed para que esta prueba pase con xito. Vamos a
ver cmo quedara (cdigo 4.12). Hay que volver a aadir un nuevo mtodo a la clase
Cell. Este mtodo tambin se muestra en 4.12
Cdigo 4.12
Mtodo seed de la clase board
public void seed(int cellIndex) {
int seeds = this.cells.get(cellIndex).removeSeeds();
for (int i = cellIndex+1; i <= seeds; i++) {
this.cells.get(i).addSeed();
}

}


Nuevo mtodo para la clase Cell
public void addSeed() {
seeds++;
}

public int removeSeeds() {
int tmp = this.seeds;
seeds = 0;
return tmp;
}

45

Tambin hemos tenido que modificar el mtodo removeSeed para que devuelva
las semillas que haba en el pozo antes de quitarlas.
En este caso, no estamos dando babysteps, sino que hemos avanzamos ms de
lo conveniente. Discutiremos este aspecto en la retrospectiva (ms adelante en este
captulo). Ahora, vamos a continuar con nuestra implementacin.
Una pausa para refactorizar
Hemos hecho dos ciclos de TDD, que nos han permitido quitar las semillas del pozo y
plantarlas en los pozos consecutivos, pero no hemos refactorizado. No vamos a avanzar
ms sin mejorar la calidad de nuestro cdigo.
Vamos a comenzar por quitar los cuatro assertEquals de la prueba del cdigo
4.11. Como las celdas se almacenan en una lista, podemos aprovechar el mtodo sublist
de la interfaz java.util.List y el mtodo assertAllCellsHasSameSeeds que creamos al
principio del captulo, para escribir una prueba ms clara y compacta en el cdigo 4.13.
Cdigo 4.13. Prueba refactorizada

@Test
public void
testSeeding4SeedsFromCell0_Cells_1_2_3_4_HasOneMoreSeeds() {
b.seed(0);

int expectedSeeds = Board.Default_Seeds+1;
List<Cell> affectedCells = b.getCells().subList(1, 5);
assertAllCellsHasSameSeeds(affectedCells, expectedSeeds);
}

Tambin usamos variables locales (expectedSeeds y affectedCells) para indicar
qu papel juega los parmetros de assertAllCellsHasSameSeeds. Vamos a continuar
refactorizando
Las pruebas que hemos escrito, dependen de que la clase Board tenga un
mtodo que devuelva una lista de objetos Cell. Si esto cambiara, afectara a todas las
pruebas que hemos escrito. Uno de los puntos fuertes que nos da TDD es que hace fcil
cambiar el cdigo, sin embargo en este caso estamos poniendo impedimentos al
cambio.
Vamos a solucionar esto centralizando en el mtodo setUp de la prueba el
acceso a la lista de celdas. Si en el futuro decidimos que la clase Board no devuelve una
lista de objetos Cell, podremos incluir el cdigo para crear dicha lista el mtodo setUp.
Cdigo 4.14. Un nico mtodo para obtener la lista de celdas.
public class TestBoard {

Board b;
List<Cell> cells;

@Before
public void setUp() {
b = new Board();
cells = b.getCells();
46

}

@Test
public void testNewBoardHas12CellsWith4SeedsEach() {
assertAllCellsHasSameSeeds(cells, Board.Default_Seeds);

assertEquals(12, cells.size());
}

@Test
public void testNewBoardHas2HousesWithoutSeeds() {
assertAllCellsHasSameSeeds(b.getHouses(), 0);

assertEquals(2, b.getHouses().size());
}

@Test
public void testSeeding4SeedsFromCell0_CellHas0Seeds() {
b.seed(0);

assertEquals(0, cells.get(0).getSeeds());
}

@Test
public void
testSeeding4SeedsFromCell0_Cells_1_2_3_4_HasOneMoreSeeds() {
b.seed(0);

int expectedSeeds = Board.Default_Seeds+1;
List<Cell> affectedCells = cells.subList(1, 5);
assertAllCellsHasSameSeeds(affectedCells, expectedSeeds);
}

public void assertAllCellsHasSameSeeds(List<Cell> l, int seeds) {
for (Cell c: l) {
assertEquals(c.getSeeds(), seeds);
}
}
}


Terminamos aqu la refactorizacin y continuamos avanzando en la
funcionalidad.
Nada que plantar
Qu sucede si en el pozo que hemos elegido no hay ninguna semilla? Parece lgico
decidir que no debe pasar nada, es decir, que el siguiente pozo no cambie el nmero de
semillas que contiene. El cdigo 4.15 expresa esto en una prueba.
Cdigo 4.15. Prueba refactorizada
@Test
public void testSeedingEmptyCell_Cell_1_DoesnotChange() {
cells.get(0).removeSeeds();

b.seed(0);

assertEquals(Board.Default_Seeds, cells.get(1).getSeeds());
}

47

La prueba anterior funciona sin necesidad de escribir cdigo adicional, ya que el
cdigo que necesitamos lo escribimos al triangular el mtodo seed en el cdigo 4.12.
Dejamos esta prueba para completar el conjunto de pruebas de la clase Board y
buscamos algo nuevo para implementar
De vuelta a la choza
Te has dado cuenta de que, hasta ahora, no hemos tenido en cuenta las chozas para
nada? Es el momento de que entren en juego.
En el Mancala, vamos plantando cada semilla en un pozo o choza siguiendo el
sentido inverso de las agujas del reloj. Teniendo los pozos y chozas en dos listas
separadas, cuando llegamos al final de los pozos de un jugador, hemos de ir a la lista de
chozas para plantar una semilla en la choza de ese jugador y luego volver a la lista de
pozos para confinar plantado las semillas restantes en los pozos del otro jugador.
Dos listas complican el cdigo y no apreciamos ninguna ventaja a tener pozos y
chozas por separado, as que vamos a almacenar pozos y chozas en una sola lista. Para
ello, modificamos las pruebas que verifican el estado inicial para reflejar este cambio. La
prueba original y la nueva prueba estn en el cdigo 4.14
Cdigo 4.14
Pruebas que borramos
@Test
public void testNewBoardHas12CellsWith4SeedsEach() {
assertAllCellsHasSameSeeds(b.getCells(), 4);

assertEquals(12, b.getCells().size());
}

@Test
public void testNewBoardHas2HousesWithoutSeeds() {
assertAllCellsHasSameSeeds(b.getHouses(), 0);

assertEquals(2, b.getHouses().size());
}

Prueba que aadimos
@Test
public void testNewBoardHas12CellsWith4SeedsEach() {
assertAllCellsHasSameSeeds(cells.subList(0, 6),
Board.Default_Seeds);
assertAllCellsHasSameSeeds(cells.subList(7, 13),
Board.Default_Seeds);

assertEquals(14, cells.size());
}

@Test
public void testNewBoardHas2HousesWithoutSeeds() {
assertEquals(0, cells.get(6).getSeeds());
assertEquals(0, cells.get(13).getSeeds());
}

48



Cdigo 4.15. Pozos y celdas en la misma lista
public class Board {

List<Cell> cells;
public static final int Default_Seeds = 4;

List<Cell> createListOfCells(int cellsNumber, int value) {
List<Cell> cells = new ArrayList<Cell>();
for (int i = 0; i < cellsNumber; i++) {
cells.add(new Cell(value));
}
return cells;
}

public Board() {
cells = this.createListOfCells(14, Default_Seeds);
cells.get(6).removeSeeds();
cells.get(13).removeSeeds();
}
//..
}

Con este cambio, la primera fila de pozos tendrn los ndices de 0 a 5, el ndice 6
ser una choza, la segunda fila de pozos tendrn los ndices de 7 a 12 y la segunda choza
ser el ndice 13. Ya podemos cambiar el cdigo para que pozos y chozas se guarden en
la misma lista.
No uses las chozas
Vamos a escribir ahora una prueba que verifique que trabajamos adecuadamente con
las chozas. La primera prueba verificar que, si se selecciona una choza, no debe
cambiar ninguna semilla de sitio (cdigo 4.16).
Cdigo 4.16.
@Test
public void testSeedingHouseCell_BoardDoesNotChange() {
int houseFirstPlayer = 6;
b.seed(houseFirstPlayer);

assertEquals(Board.Default_Seeds,
cells.get(houseFirstPlayer + 1).getSeeds());
}

La primera vez que hice y ejecut esta prueba funcion correctamente cuando
no debera haberlo hecho. Tuve que desarrollar varias pruebas para darme cuenta de
cul haba sido el error. Eres capaz de encontrar el error en el cdigo 4.12? Recuerda
que la prueba del cdigo 4.11 funciona correctamente.
49

Vamos a explicar el error del cdigo 4.12 a continuacin. Como el parmetro
cellIndex siempre vale 4, en la prueba solo plantbamos desde el pozo 1 hasta la 4. Si
quisiramos sembrar las semillas del pozo 1 en vez de el pozo 0, el cdigo solo plantaba
en los pozos 2, 3 y 4. Si intentramos sembrar las semillas del pozo 5 en adelante, el
cdigo no plantara en ningn pozo ya que el bucle for no llegaba a dar ninguna vuelta.
Encontrarme este fallo me hizo pensar de nuevo en algo que ya hemos
mencionado. Hemos escrito demasiado cdigo cuando implementamos el bucle de
seed? No podramos haber dado pasos ms pequeos? Retomaremos estas preguntas
en la retrospectiva.
Una vez arreglado el error e implementado el cdigo necesario para que la
prueba del cdigo 4.16 pase con xito, nos queda el cdigo 4.17.
Cdigo 4.17.
public void seed(int cellIndex) {
if (cellIndex == 6)
return;

int seeds = this.cells.get(cellIndex).removeSeeds();

int initialCell = cellIndex+1;
int finalCell = cellIndex + seeds;

for (int i = initialCell; i <= finalCell; i++) {
this.cells.get(i).addSeed();
}

}

A continuacin podramos escribir una prueba similar para evitar que se utilice la
otra choza. No vamos a mostrar dicho cdigo. Ya estamos a punto de acabar con toda la
funcionalidad. Vamos a darle un ltimo empujn
De la ltima a la primera
La ltima funcionalidad pendiente es que, cuando plante en la ltima celda (el pozo del
segundo jugador) y an queden semillas por sembrar, se contine plantando por la
primera celda. En otras palabras, despus del objeto Cell del ndice 13 vendra el objeto
Cell del ndice 0. El nmero de vueltas del bucle viene determinado por las semillas a
plantar.
Vamos a definir una prueba que muestre este escenario en el cdigo 4.18.
Cdigo 4.18. Seleccionamos el ltimo pozo y la semillas se plantan en los primeros
@Test
public void testSeedingLasCell_SeedsArePlantedInFirstPits() {
b.seed(12);

assertEquals(1, this.cells.get(13).getSeeds());
assertAllCellsHasSameSeeds(this.cells.subList(0, 3),
Board.Default_Seeds+1);
50

}

Solo tenemos que aadir una condicin al mtodo seed para que, si llega al final
vuelva al principio. El resultado se muestra en el cdigo 4.19.
Cdigo 4.19.
public void seed(int cellIndex) {
if (cellIndex == 6)
return;

int seeds = this.cells.get(cellIndex).removeSeeds();

int initialCell = cellIndex+1;
int finalCell = cellIndex + seeds;

int index;
for (int i = initialCell; i <= finalCell; i++) {
index = i % 14;
this.cells.get(index).addSeed();
}

}

Ya hemos terminado con todo lo que tenamos hacer, ya no hay ms
funcionalidad nueva que implementar.
La ltima refactorizacin
Las pruebas anteriores son muy dependientes de detalles de implementacin, como el
rango de ndices necesarios para un grupo de pozos. Por este mismo motivo, nuestras
pruebas tambin son difciles de entender. Si empezamos a trabajar en otro proyecto y
volvemos a este cdigo pasada una semana, entenderemos qu significa cells.sublist(0,
6)? Por qu una sublista? Por qu exactamente ese rango?
Podemos corregir esto buscando nuevas abstracciones. Una de ellas es el
concepto de jugador. El primer jugador ser propietario de algunos de los pozos (del 0 al
6 para incluya su choza) y el segundo jugador ser propietario de los dems pozos (del 7
al 13). Vemos como queda esta refactorizacin en el 4.18.
Cdigo 4.17
Prueba utilizando ndices
@Test
public void testNewBoardHas12CellsWith4SeedsEach() {
List<Cell> cellsFirstPlayer = cells.subList(0, 6);
assertAllCellsHasSameSeeds(cellsFirstPlayer,
Board.Default_Seeds);

List<Cell> cellsSecondPlayer = cells.subList(7, 13);
assertAllCellsHasSameSeeds(cellsSecondPlayer,
Board.Default_Seeds);

assertEquals(14, cells.size());
}
51

Misma prueba utilizando jugadores
@Test
public void testNewBoardHas12CellsWith4SeedsEach() {
assertAllCellsHasSameSeeds(b.getCellsFirstPlayer(),
Board.Default_Seeds);
assertAllCellsHasSameSeeds(b.getCellsSecondPlayer(),
Board.Default_Seeds);

assertEquals(14, cells.size());
}

Podemos hacer lo mismo para las chozas, como se muestra en el cdigo 4.18.
Cdigo 4.18.
Prueba utilizando ndices
@Test
public void testNewBoardHas2HousesWithoutSeeds() {
assertEquals(0, cells.get(6).getSeeds());
assertEquals(0, cells.get(13).getSeeds());
}
Mima prueba utilizando jugadores
@Test
public void testNewBoardHas2HousesWithoutSeeds() {
assertEquals(0, b.getHouseFirstPlayer().getSeeds());
assertEquals(0, b.getHouseSecondPlayer().getSeeds());
}

Vamos a utilizar esta abstraccin para introducir ndices relativos a los
jugadores. Es decir, ahora tendremos el pozo 0 del primero jugador y el pozo 0 del
segundo jugador (que ser el ndice 7 de la lista de la clase Board). De esta manera las
pruebas quedaran as (cdigo 4.19).
Cdigo 4.19
Prueba utilizando ndices
@Test
public void
testSeeding4SeedsFromCell0_Cells_1_2_3_4_HasOneMoreSeeds() {
b.seed(0);

int expectedSeeds = Board.Default_Seeds+1;
List<Cell> affectedCells = cells.subList(1, 5);
assertAllCellsHasSameSeeds(affectedCells, expectedSeeds);
}
Prueba utilizando jugadores
@Test
public void
testSeeding4SeedsFromCell0_Cells_1_2_3_4_HasOneMoreSeeds() {
b.seed(0);

int expectedSeeds = Board.Default_Seeds+1;
List<Cell> affectedCells =
b.getCellsFirstPlayer().subList(1, 5);
assertAllCellsHasSameSeeds(affectedCells, expectedSeeds);
}
52


@Test
public void
testSeeding4SeedsFromCell_7_Cells_8_9_10_11_HasOneMoreSeeds() {
b.seed(7);

int expectedSeeds = Board.Default_Seeds+1;
List<Cell> affectedCells =
b.getCellsSecondPlayer().subList(1, 5);
assertAllCellsHasSameSeeds(affectedCells, expectedSeeds);
}

Podramos continuar refactorizando el cdigo. A continuacin se exponen
algunas ideas de posibles refactorizaciones.
Podramos aadir un mtodo que devuelva slo los pozos de un jugador y otro
mtodo que devuelva slo las chozas, pero si quisiramos escribir una prueba que
comenzara plantando en el pozo de un jugador y terminara plantando en el pozo del
otro jugador, tendramos que hacer tres llamadas, una obtener los pozos del primer
jugador y comprobar el incremento de semillas, otra para obtener la choza y otra para
obtener los pozos del segundo jugador.
Otra alternativa podra ser ocultar todo lo anterior tras un mtodo assert que,
por ejemplo, tomara como entrada la celda original y la cantidad de celdas (incluida la
choza) que queremos verificar.

Si despus de terminar de escribir cdigo con TDD necesitamos una gran refactorizacin
como la que hemos hecho en esta seccin, es probable que no hayamos aplicado bien
TDD.


A pesar de estas refactorizaciones, ni el proceso que hemos seguido ha sido
todo lo sencillo y elegante que debera ser aplicando TDD, ni la solucin ha quedado
sencilla. Vamos a analizar los fallos y vamos a intentar corregirlos.
Retrospectica de la primera solucin
Los problemas que hemos encontrado y que vamos a analizar en esta seccin son.
Exponer detalles de implementacin y dependencia de los ndices.
Confundir el concepto de pocas lneas de cdigo con el concepto de
babysteps.
Implementar dos clases a la vez con un nico conjunto de pruebas.
No detectar el mal olor de incumplir la ley de Demeter.
Separacin del dominio del problema y de la solucin.
No hemos utilizado un diario de diseo.
Durante todo el desarrollo hemos sido muy dependientes de listas e ndices.
Hemos necesitado saber, por ejemplo, los ndices que correspondan a las chozas y los
53

pozos. Hemos expuesto detalles de implementacin desde el principio (la lista de
celdas).
Tampoco hemos codificado en babysteps de manera estricta. Hemos adelantado
demasiado e implementamos cdigo no cubierto por pruebas. Por ejemplo en la seccin
Haced sitio, slo tenamos que escribir el cdigo para quitar las semillas del pozo 0 y
aadirlas en los pozos 1, 2, 3, y 4. Sin embargo escribimos una solucin general para
cualquier escenario.
Esta solucin funciona para todos los pozos? Es la solucin ms adecuada?
Hemos necesitado ms pruebas para verificar que la implementacin del mtodo seed
funciona en otros escenarios (que ya podemos ver que no funciona porque no tiene en
cuenta las chozas). No cuenta solo la cantidad del cdigo sino la complejidad del mismo.
Este caso ilustra como unas pocas lneas complejas pueden dar lugar a errores
inesperados, como nos pas, ya que no comprobamos que estuviramos calculando
correctamente el lmite del bucle.
Una solucin ms adecuada hubiese sido empezar con la estrategia fake y aadir
una segunda prueba para evolucionar el cdigo (estrategia de triangulacin). Esta
segunda prueba habra detectado el error en el bucle.
Hemos estado trabajando con dos clases simultneamente, las clases: Board y
Cell. Sin embargo, solo hemos escrito pruebas para la clase Board (salvo la primera
prueba de la kata) y no hemos escrito pruebas unitarias para la clase Cell a pesar de
aadir y modificar su cdigo durante la kata. Hemos utilizado las pruebas de Board para
probar el cdigo de Cell. Haber escrito una prueba para Cell antes de implementar la
prueba para Board no hubiese sido la solucin ms adecuada. Veamos porqu.
Desde el principio, el autor de esta solucin tena un diseo en su mente, con
una clase Board y una clase Cell y ha querido implementarlo aplicando TDD. Empezar a
aplicar TDD con un posible diseo en la mente no es malo. TDD nos avisa de si este
diseo funciona o no. El problema es cuando nos centramos en nuestra idea y no
escuchamos a TDD, y esto es lo que ha pasado.
En las secciones anteriores, TDD nos estaba avisando mediante las pruebas (y el
mal olor que veremos continuacin) de que un cambio en Board implicaba un cambio en
Cell. Ambas clases estn tan ligadas que no tiene sentido considerarlas por separado,
por lo que Cell sera una clase interna de Board.

TDD te obliga a escribir todo el cdigo en una nica clase para extraerlo, mediante
refactorizaciones. TDD es muy til cuando no tenemos claro qu clases construir, pero si
ya sabemos las clases que necesitamos: adelante. Con TDD descubrirs si tu diseo es
fcil de usar y probar.


Por si lo anterior no fuera suficiente, mantener ambas clases separadas
incumple la ley de Demeter e introduce un mal olor en nuestro cdigo que nos ha
acompaado durante todo el desarrollo. Puedes ver este mal olor, por ejemplo (por
54

ejemplo en el cdigo 4.8) o en el cdigo 4.2 que repetimos a continuacin. Repasemos el
cdigo 4.2 dnde tambin aparece este mal olor.
Cdigo 4.2.
@Test
public void
testSeeding4SeedsFromCell0_Cells_1_2_3_4_HasOneMoreSeeds() {
b.seed(0);

assertEquals(seeds+1, b.getCells().get(1).getSeeds());
assertEquals(seeds+1, b.getCells().get(2).getSeeds());
assertEquals(seeds+1, b.getCells().get(3).getSeeds());
assertEquals(seeds+1, b.getCells().get(4).getSeeds());

}

La ley de Demeter nos dice, de manera resumida, que un objeto solo debe
comunicarse con sus amigos inmediatos, es decir, con los objetos de los que tiene
dependencia directa, como parmetros y atributos. En el cdigo 4.2, el objeto que
contiene los mtodos de prueba tiene una dependencia del objeto de la clase Board.
Este objeto, a su vez, depende de objetos de la clase Cell (imagen 03). Sin embargo la
prueba no depende de Cell por lo que no debera comunicarse con estos objetos, cosa
que s est pasando en el cdigo 4.2.
Imagen 03. Dependencias de las clases




Este mal olor aparece cuando separo la lgica de negocio los datos que dicha
lgica de negocios necesita. Es fcil de detectar cuando descubrimos que tenemos que
llamar a un mtodo que devuelve un objeto sobre el que llamamos a otro mtodo que
devuelve otro objeto, y as varias veces hasta que, al final, llegamos al valor que
buscbamos.
Esto aumenta la dependencia de otros objetos, lo que hace al cdigo ms difcil
de utilizar y de cambiar. Una buena estrategia para evitar este mal olor es mover la
lgica a quin tiene los datos. Lo correcto hubiera sido que el propio tablero nos
devolviera el nmero de semillas, por ejemplo con un mtodo getSeedInCell(int index),
en lugar del propio objeto Cell.
Otro problema que puede parecer trivial, pero no lo es en absoluto, es la
separacin entre conceptos del dominio del problema y el dominio de la solucin. En el
enunciado de la kata hablamos de chozas y pozos, pero la clase que las implementan se
llama Cell.
Te has perdido en algn momento de la explicacin o te has confundido porque
hablamos de objetos Cell en vez de pozos (o viceversa)? Si esto sucede esto en un
55

ejemplo tan sencillo imagnate en un proyecto con varias personas y clientes de verdad.
Si cada uno habla en su propia jerga ser difcil entenderse y los malentendidos estarn
al orden del da, adems del tiempo que se pierde traduciendo conceptos que, en
verdad, son los mismos. Lo mejor es intentar utilizar los mismos conceptos del problema
en el cdigo.
Por ltimo, no hemos utilizado el diario de diseo en ningn momento. No s si
t, lector, te habrs sentido perdido en algn momento durante las secciones
anteriores. Te puedo asegurar que yo s me he sentido perdido en algn momento y
que, a veces, al final de una implementacin o una refactorizacin, he tenido que volver
a consultar el enunciado para hacerme una idea de qu quedaba por implementar.
Como ves, hay varias cosas que no han funcionado a pesar de haber aplicado
TDD. Esto no es malo, al contrario, hemos aprendido muchos detalles interesantes.
Vamos a repetir el ejercicio aprovechando la experiencia que ya tenemos y aplicando
mejor las ideas de TDD que vimos en el primer captulo y en los ejercicios anteriores.
Un nuevo comienzo.
Vamos a repetir la kata Mancala, poniendo en prctica lo que hemos aprendido,
evitando los errores que hemos cometido y aplicando mejor los consejos que vimos en
el primer captulo. Por ello, lo primero que hacemos es un diario de diseo con todo lo
que queremos implementar en esta cada.
4.1. Diario de diseo
Inicialmente, el tablero tiene 6 pozos por cada jugador, 12 en total y dos chozas
por cada jugador (para un total de 14 casillas).
Cada pozo tiene 4 semillas y las chozas estn vacas.
El primer jugador puede plantar cualquiera de sus pozos que tenga al menos
una semilla.
El segundo jugador puede plantar cualquiera de sus pozos que tenga al menos
una semilla.
Cuando un jugador planta un pozo, ese pozo queda sin semillas.
Cuando se planta en el ltimo pozo (choza del segundo jugador) y an quedan
semillas, se contina plantando desde el principio (primer pozo del primer
jugador).
Si se intenta sembrar un pozo no vlido, el Mancala no cambia.

Al igual que en la implementacin anterior, vamos a comenzar definiendo el
estado inicial del Mancala.
Nuestra segunda primera prueba.
Empezamos escribiendo una prueba que verifique el estado inicial del tablero, pero
ahora vamos a intentar independizarnos lo mximo posible de la implementacin y los
atributos del tablero. Cmo podemos hacerlo?
56

En Java todos los objetos tienen un mtodo toString cuya misin es devolver
una cadena de texto que represente al objeto. Podemos utilizar esta cadena de texto
para representar el estado del Mancala, cmo muestra el cdigo 4.20.
Cdigo 4.20
Prueba.
public class TestMancala {
@Test
public void testManacalaInicial() {
String expected = "0, 4, 4, 4, 4, 4, 4\n"
+ "4, 4, 4, 4, 4, 4, 0";

Mancala m = new Mancala();
String mancalaState = m.toString();

assertEquals(mancalaState, expected);
}
}
Implementacin fake
public class Mancala {
@Override
public String toString() {
return "0, 4, 4, 4, 4, 4, 4\n"
+ "4, 4, 4, 4, 4, 4, 0";
}

}

En el cdigo 4.20 hemos utilizado la estrategia fake y hemos declarado
directamente la cadena de texto que representa el Mancala inicial en el mtodo toString
para que la prueba pase con xito.
Utilizando una cadena de texto nos aislamos de los detalles de implementacin
de la clase Mancala y definimos en una sola prueba el nmero de casillas, las semillas
iniciales y el estado de las chozas.
Ahora podramos refactorizar para construir esa cadena a partir del estado de
los pozos y las chozas, pero vamos a dejarlo as y vamos a ver, en la prxima seccin un
error muy comn cuando se empieza a programar en TDD.
Un error comn. Triangular el fake
Vamos a abordar ya la prueba de sembrar y vamos a comenzar, de nuevo, por
comprobar que, cuando sembramos, el pozo elegido se queda sin semillas. Escribimos
una prueba utilizando la representacin del estado del Manca como una cadena de
texto en el cdigo 4.21.
Cdigo 4.21.

@Test
public void testSiembroUnPozo_EsePozoQuedaSinSemillas() {
String expected = "0, 4, 4, 4, 4, 4, 4\n"
+ "0, 4, 4, 4, 4, 4, 0";

57

Mancala m = new Mancala();
m.seed(Player.FIRST_PLAYER, 0);
String mancalaState = m.toString();

assertEquals(mancalaState, expected);
}


A continuacin, vamos a escribir el mnimo cdigo posible para que dicha
prueba pase (cdigo 4.22).
Cdigo 4.22.

public class Mancala {

String s = "0, 4, 4, 4, 4, 4, 4\n"
+ "4, 4, 4, 4, 4, 4, 0";


@Override
public String toString() {
return s;
}

public void seed(Player firstPlayer, int i) {
s = "0, 4, 4, 4, 4, 4, 4\n"
+ "0, 4, 4, 4, 4, 4, 0";

}
}


Aunque la prueba pasa correctamente, el cdigo no ha evolucionado bien. En
este caso (cdigo 4.22) hemos triangulado el propio fake, en lugar de triangular el
cdigo real. La manera de arreglar este error es refactorizar antes y quitar el fake,
aadiendo un autntico estado a la clase Mancala.
Refactorizar y avanzar
Volvamos al punto en el que estbamos justo despus de terminar el primer ciclo de
TDD (cdigo 4.20) y refactoricemos. Es importante que esta refactorizacin no
introduzca apenas cdigo nuevo, ya que si usamos las refactorizaciones para escribir el
cdigo que no hemos escrito en el momento de hacer que una prueba pase de rojo a
verde estaremos aplicando mal TDD.
En este caso, lo ms sencillo parece utilizar una tabla. Cada posicin de la tabla
representa a un jugador y contiene una segunda tabla con todos los pozos de dicho
jugador, incluyendo las chozas. Los ceros representan las chozas y las dos series de 4s los
pozos de ambos jugadores con sus semillas iniciales.
Cdigo 4.23.

public class Mancala {
int[] pits = {0, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4};

58

@Override
public String toString() {
return Arrays.toString(pits);
}
}


Java no genera directamente una cadena con los elementos de un array, as que
utilizamos el mtodo toString de la clase Arrays que ya viene en el API de Java. Sin
embargo, este mtodo devuelve una cadena con un formato distinto a la cadena que
habamos escrito en la prueba, por lo que tenemos que modificar la prueba como
muestra el cdigo 4.24.
Cdigo 4.24. Cambiamos la cadena de texto para que la prueba pase con xito

@Test
public void testManacalaInicial() {
String expected = "[0, 4, 4, 4, 4, 4, 4, "
+ "0, 4, 4, 4, 4, 4, 4]";

Mancala m = new Mancala();
String mancalaState = m.toString();

assertEquals(mancalaState, expected);
}
}

Ya podemos tachar nuestras primeras tareas del diario de diseo.
4.1. Diario de diseo
Inicialmente, el tablero tiene 6 pozos por cada jugador, 12 en total y dos chozas
por cada jugador (para un total de 14 casillas).
Cada pozo tiene 4 semillas y las chozas estn vacas.
El primer jugador puede plantar cualquiera de sus pozos que tenga al menos
una semilla.
El segundo jugador puede plantar cualquiera de sus pozos que tenga al menos
una semilla.
Cuando un jugador planta un pozo, ese pozo queda sin semillas.
Las y Cuando un jugador planta un pozo, el resto de pozos y chozas gana una
Cuando se planta en el ltimo pozo (choza del segundo jugador) y an quedan
semillas, se contina plantando desde el principio (primer pozo del primer
jugador).
Si se intenta sembrar un pozo no vlido, el Mancala no cambia.
Triangulando la siembra
Ahora ya podemos escribir una prueba para sembrar semillas y comenzar a triangular la
siembra. La prueba que escribimos justo antes de triangular el fake est en el cdigo
4.25.

59

Cdigo 4.25.

@Test
public void testSiembroUnPozo_EsePozoQuedaSinSemillas() {
String expected = "[0, 0, 4, 4, 4, 4, 4, "
+ "0, 4, 4, 4, 4, 4, 4]";

Mancala m = new Mancala();
m.seed(Player.FIRST_PLAYER, 0);
String mancalaState = m.toString();

assertEquals(mancalaState, expected);
}


Vamos a comentar algunos detalles de esta prueba. En primer lugar estamos
utilizando ndices individuales para cada celda, es decir, el primer jugador tendr sus
celdas en los ndices de 0 a 5 y el segundo jugador tambin tendr sus celdas en esos
mismos ndices. En el array, las celdas del primer jugador sern las primeras (ndices del
1 al 7) y las del segundo las segundas (ndices del 9 al 14).
Esta prueba no es correcta del todo, ya que adems de quedar a cero el pozo
elegido, los cuatro pozos siguientes incrementan sus semillas en 1, pero es suficiente
para continuar avanzando. Ms adelante aadiremos ms informacin a esta prueba.
El cdigo mnimo para la prueba anterior es el cdigo 4.26 (recuerda que el
primer pozo del primer jugador est en el ndice 1 de la tabla pits).
Cdigo 4.26.
public void seed(Player player, int pitIndex) {
this.pits[1] = 0;
}

Sin embargo la prueba del cdigo 4.25 no pasa con xito ya que hemos
declarado la tabla pit como constante y sus valores no se modifican. Vamos a dejar un
momento esta prueba de lado y a refactorizar la creacin de la tabla como se muestra
en el cdigo 4.27. Tambin vamos a refactorizar la prueba para calcular el ndice a partir
del parmetro de entrada
Cdigo 4.27.
Refactorizacin para crear un array modificable.
int [] pits;
private final static int PLAYERS = 2;
private final static int PITS = 7;
private final static int INITIALSEEDS = 4;


public Mancala() {
pits = new int[PLAYERS * PITS];
Arrays.fill(pits, INITIALSEEDS);
pits[0] = 0;
pits[7] = 0;
}
Refactorizacin para calcular el ndice a partir del parmetro.
public void seed(Player player, int pitIndex) {
this.pits[pitIndex + 1] = 0;
}
60


Con esta refactorizacin las dos pruebas que tenemos pasan con xito y
terminamos otra de las tareas de nuestro diario de diseo.
4.1. Diario de diseo
Inicialmente, el tablero tiene 6 pozos por cada jugador, 12 en total y dos chozas
por cada jugador (para un total de 14 casillas).
Cada pozo tiene 4 semillas y las chozas estn vacas.
El primer jugador puede plantar cualquiera de sus pozos que tenga al menos
una semilla.
El segundo jugador puede plantar cualquiera de sus pozos que tenga al menos
una semilla.
Cuando un jugador planta un pozo, ese pozo queda sin semillas.
Cuando se planta en el ltimo pozo (choza del segundo jugador) y an quedan
semillas, se contina plantando desde el principio (primer pozo del primer
jugador).
Si se intenta sembrar un pozo no vlido, el Mancala no cambia.
Triangulando pruebas y cdigo
Vamos a implementar la funcionalidad de aadir las semillas sembradas en los pozos
siguientes. No tiene sentido escribir una nueva prueba, ya que la condicin de la prueba
4.25 nunca ser cierta porque no estamos comprobando el incremento de los pozos. En
este caso es mejor modificar dicha prueba para incorporar la nueva funcionalidad, como
hemos hecho en el cdigo 4.28.
Cdigo 4.28. Aadimos que los 4 pozos siguientes tengan 1 semilla ms.
@Test
public void
testPrimerJugadorSiembraSuPrimerPozo_EsePozoQuedaSinSemillas_y_LosCuatroP
ozosSiguientesPasanA5() {
String expected = "[0, 0, 5, 5, 5, 5, 4, " +
"4, 4, 4, 4, 4, 4, 0]";

m.seed(Player.FIRST_PLAYER, FIRST_PIT);
String mancalaState = m.toString();

assertEquals(expected, mancalaState);
}

La implementacin en babystep es un fake (cdigo 4.29). La prueba pasa
correctamente.
Cdigo 4.29.
public void seed(Player player, int pitIndex) {
this.pits[pitIndex + 1] = 0;

this.pits[pitIndex + 2]++;
this.pits[pitIndex + 3]++;
this.pits[pitIndex + 4]++;
this.pits[pitIndex + 5]++;
}
61


Las prximas pruebas van a triangular el mtodo seed, por lo que vamos a
refactorizarlo para que sea ms sencillo de modificar. El primer cambio es reemplazar
los cuatro incrementos por un bucle. A diferencia de la solucin anterior, las cuatro
lneas repetidas del cdigo 4.29 me indican cules son los ndices de inicio y fin correctos
para el bucle (cdigo 4.30).
Cdigo 4.30. Aadimos un bucle para icnrementar los pozos.
public void seed(Player player, int pitIndex) {

this.pits[pitIndex + 1] = 0;

for (int i = pitIndex + 2; i <= (pitIndex + 4); i++) {
this.pits[i]++;
}
}

Esta prueba solo funcionar cuando sembremos un pozo que contenga
exactamente cuatro semillas, as que no podemos dar esta funcionalidad por concluida.
Vamos a seguir triangulando con una nueva prueba, por ejemplo que intente sembrar
un pozo sin semillas (cdigo 4.31).
Cdigo 4.31. Sembramos un pozo sin semillas.
@Test
public void
testPrimerJugadorSiembraUnPozoConCeroSemillas_ElMancalaQuedaIgual() {
String expected = "[0, 0, 5, 5, 5, 5, 4, " +
"0, 4, 4, 4, 4, 4, 4]";

m.seed(Player.FIRST_PLAYER, FIRST_PIT);
m.seed(Player.FIRST_PLAYER, FIRST_PIT);
String mancalaState = m.toString();

assertEquals(expected, mancalaState);
}

La prueba falla, por lo que es el momento de modificar el mtodo seed para que
solo se siembren las semillas del pozo de origen indicado por pitInde4. El resultado se
muestra en el cdigo 4.32.
Cdigo 4.32..
public void seed(Player player, int pitIndex) {
int seeds = this.pits[pitIndex + 1];
this.pits[pitIndex + 1] = 0;

for (int i = pitIndex + 2; i <= (pitIndex + 4); i++) {
this.pits[i]++;
}
}

Vamos a hacer dos refactorizaciones para terminar. En la primera, movemos el
cdigo que quita las semillas del pozo a un mtodo auxiliar y, en la segunda, aadimos
una nueva variable local que incremente el ndice del pozo en 1. El cdigo 4.33 muestra
lo que llevamos hasta ahora despus de hacer esas refactorizaciones.
62

Cdigo 4.33. Mtodo seed refactorizado
public void seed(Player player, int pitIndex) {
int pit = pitIndex + 1;
int seeds = removeSeedsFrom(pit);

for (int i = pit +1; i <= (pit + seeds); i++) {
this.pits[i]++;
}
}

private int removeSeedsFrom(int pit) {
int seeds = this.pits[pit];
this.pits[pit] = 0;

return seeds;
}

Hasta aqu hemos conseguido que el primer jugador pueda plantar sus pozos,
pero este cdigo no funcionar con el segundo jugador.
4.1. Diario de diseo
Inicialmente, el tablero tiene 6 pozos por cada jugador, 12 en total y dos chozas
por cada jugador (para un total de 14 casillas).
Cada pozo tiene 4 semillas y las chozas estn vacas.
El primer jugador puede plantar cualquiera de sus pozos que tenga al menos
una semilla.
El segundo jugador puede plantar cualquiera de sus pozos que tenga al menos
una semilla.
Cuando un jugador planta un pozo, ese pozo queda sin semillas.
Cuando se planta en el ltimo pozo (choza del segundo jugador) y an quedan
semillas, se contina plantando desde el principio (primer pozo del primer
jugador).
Si se intenta sembrar un pozo no vlido, el Mancala no cambia.

Implantando el segundo jugador
Para completar nuestro cdigo y que soporte al segundo jugador vamos a escribir una
prueba que siembre un pozo del segundo jugador (cdigo 4.34).
Cdigo 4.34. Primera prueba para el segundo jugador
@Test
public void testSegundoJugadorSiembraSuPrimerPozoCon_4_Semillas()
{
String expected = "[0, 4, 4, 4, 4, 4, 4, "
+ "0, 0, 5, 5, 5, 5, 4]";

m.seed(Player.SECOND_PLAYER, FIRST_PIT);
String mancalaState = m.toString();

assertEquals(expected, mancalaState);
}

63

La prueba anterior falla porque el mtodo seed siembra el pozo del primer
jugador. Para corregir esto, el mtodo debe detectar si queremos sembrar en el pozo
del segundo jugador y modificar el ndice de la tabla pits. En este caso hacemos una
implementacin obvia que se muestra en el cdigo 4.35.
Cdigo 4.35. Un condicional cambia el ndice si es el segundo jugador.
public void seed(Player player, int pitIndex) {
int pit = pitIndex + 1;

if (player == Player.SECOND_PLAYER) {
pit += 7;
}

int seeds = removeSeedsFrom(pit);

for (int i = pit +1; i <= (pit + seeds); i++) {
this.pits[i]++;

}
}

Con el aadido del cdigo 4.34 el segundo jugador ya puede sembrar sus pozos.
Como refactorizacin, vamos a extraer la funcionalidad de calcular el ndice correcto a
un mtodo auxiliar (cdigo 4.36).
Cdigo 4.36.
public void seed(Player player, int pitIndex) {
int pit = calculatePit(player, pitIndex);
int seeds = removeSeedsFrom(pit);

for (int i = pit +1; i <= (pit + seeds); i++) {
this.pits[i]++;

}
}

private int calculatePit(Player player, int pitIndex) {
int pit = pitIndex + 1;

if (player == Player.SECOND_PLAYER) {
pit += PITS;
}
return pit;
} }

Actualizamos el diario de diseo antes de continuar.
4.1. Diario de diseo
Inicialmente, el tablero tiene 6 pozos por cada jugador, 12 en total y dos chozas
por cada jugador (para un total de 14 casillas).
Cada pozo tiene 4 semillas y las chozas estn vacas.
El primer jugador puede plantar cualquiera de sus pozos que tenga al menos
una semilla.
El segundo jugador puede plantar cualquiera de sus pozos que tenga al menos
una semilla.
Cuando un jugador planta un pozo, ese pozo queda sin semillas.
64

Cuando se planta en el ltimo pozo (choza del segundo jugador) y an quedan
semillas, se contina plantando desde el principio (primer pozo del primer
jugador).
Si se intenta sembrar un pozo no vlido, el Mancala no cambia.
Del final al principio
Ya queda poco por implementar en nuestro diario de diseo. En esta seccin vamos a
implementar la funcionalidad necesaria para que siga plantando por el principio cuando
llegue al final y an quedan semillas. Esto pasa, por ejemplo, si desde el Mancala inicial,
el segundo jugador planta su primer pozo (prueba del cdigo 4.37).
Cdigo 4.37. El segundo jugador planta su ltimo pozo.
@Test
public void
testSiSeLlegaAlUltimoPozoYAunQuedanSemillasSeContinuaPlantandoPorElPrime
rPozo() {
String expected = "[1, 5, 5, 5, 4, 4, 4, "
+ "0, 4, 4, 4, 4, 4, 0]";

m.seed(Player.SECOND_PLAYER, LAST_PIT);
String mancalaState = m.toString();

assertEquals(expected, mancalaState);
}


La prueba falla ya que el ndice de la tabla pits se dale de los lmites. Como ya
vimos en la solucin anterior una manera sencilla de calcular el ndice correcto, vamos a
reutilizarla y la aadirnos como una implementacin obvia al mtodo seed (cdigo 4.38).
Cdigo 4.38.
public void seed(Player player, int pitIndex) {
int pit = calculatePit(player, pitIndex);
int seeds = removeSeedsFrom(pit);

for (int i = pit +1; i <= (pit + seeds); i++) {
this.pits[(i % 14)]++;

}
}

Con esta modificacin todas las pruebas pasan correctamente y ya solo nos
queda una ltima funcionalidad en nuestro diario de diseo.
65

Solo pozos vlidos
La ltima funcionalidad que nos queda en el diario de diseo es verificar que el mtodo
seed recibe el ndice un pozo vlido. En este caso el ndice es vlido si est comprendido
entre 0 y 5 ambos inclusive.
4.1. Diario de diseo
Inicialmente, el tablero tiene 6 pozos por cada jugador, 12 en total y dos chozas
por cada jugador (para un total de 14 casillas).
Cada pozo tiene 4 semillas y las chozas estn vacas.
El primer jugador puede plantar cualquiera de sus pozos que tenga al menos
una semilla.
El segundo jugador puede plantar cualquiera de sus pozos que tenga al menos
una semilla.
Cuando un jugador planta un pozo, ese pozo queda sin semillas.
Cuando se planta en el ltimo pozo (choza del segundo jugador) y an quedan
semillas, se contina plantando desde el principio (primer pozo del primer
jugador).
Si se intenta sembrar un pozo no vlido, el Mancala no cambia.

Para implementar esta funcionalidad en nuestro cdigo, primero escribiramos
una prueba con un ndice menor que cero, la haramos pasar con xito y luego
repetiramos con una prueba con un ndice mayor que 5. Como es una tarea sencilla,
vamos a ver ambas pruebas juntas en el cdigo 4.39.
Cdigo 4.39. Pruebas para verificar que no pasa nada con un pozo incorrecto
ndice menor que cero.
@Test
public void testSiSeleciconoUnPozoMenorQueCeroNoPasaNada() {
String expected = "[0, 4, 4, 4, 4, 4, 4, "
+ "0, 4, 4, 4, 4, 4, 4]";

m.seed(Player.FIRST_PLAYER, -1);
String mancalaState = m.toString();

assertEquals(expected, mancalaState);
}
ndice mayor que cinco.
@Test
public void testSiSeleciconoUnPozoMayorQueElMaximo_NoPasaNada() {
String expected = "[0, 4, 4, 4, 4, 4, 4, "
+ "0, 4, 4, 4, 4, 4, 4]";

m.seed(Player.SECOND_PLAYER, LAST_PIT + 1);
String mancalaState = m.toString();

assertEquals(expected, mancalaState);
}

66

El cdigo (ya refactorizado) que hace pasar con xito las pruebas anteriores se
muestra en el cdigo 4.40.
Cdigo 4.40.
public void seed(Player player, int pitIndex) {
if (!isValid(pitIndex))
return;

int pit = calculatePit(player, pitIndex);
int seeds = removeSeedsFrom(pit);

for (int i = pit +1; i <= (pit + seeds); i++) {
this.pits[i]++;

}
}

private boolean isValid(int pitIndex) {
return (pitIndex >= 0) && (pitIndex <= 5);
}

Y con esto terminamos la segunda implementacin de la kata Mancala. Vamos a
resumir las conclusiones de esta segunda solucin y, despus, compararemos ambas
implementaciones.
Retrospectica de la segunda solucin
En esta segunda solucin todo ha ido mejor que en la primera. Hemos aplicado TDD de
manera ms correcta y hemos tenido menos dificultades escribiendo pruebas e
implementando el cdigo. Tambin hemos encontrado menos errores inesperados. Pero
parte de este xito tambin se debe a lo que aprendimos implementando la kata
Mancala por primera vez. Gracias a la primera solucin por ejemplo, hemos separado
desde el principio el ndice identificaba una celda del ndice interno, o hemos utilizado
directamente la frmula para calcular los ndices.
No debes desanimarte cuando un fragmento de cdigo no salga a la primera. Un
fallo es una oportunidad para mejorar tu experiencia con TDD y para descubrir
caractersticas importantes del cdigo que ests implementando y que podr sutilizar en
un segundo intento. Si queremos que todo salga bien a la primera y no estamos
dispuestos a quitar el cdigo que ha salido mal y volverlo a intentar nos estamos
cerrando la puerta a mejorar.
El principal problema principal de esta segunda solucin es la dependencia de
las cadenas de texto. Aunque usar una cadena de texto nos asla de los detalles de
implementacin, tambin introduce una fragilidad en las pruebas. Un pequeo cambio
en la cadena (una coma, un espacio) invalida todas las pruebas. Este no es un motivo de
peso para no usar esta estrategia, sino para mejorarla con tcnicas y herramientas
adicionales, por ejemplo TestText. Puede seguir practicando con esta estrategia
implementado la kata MineSweeper que puedes encontrar en Internet.
67

Comparacin entre ambas soluciones.
Vamos a comparar ambas soluciones (la primera solucin utilizando una lista y la clase
Cell y la segunda solucin utilizando una tabla de enteros). Para ello, hemos calculado el
conjunto de mtricas de cdigo que se muestra en la tabla 1. El primer nmero indica el
valor medio y el segundo indica el valor mximo
Tabla 1. Mtricas de las dos soluciones.
Mtrica Primera solucin Segunda solucin
Average Block Depth 0.96 / 2 0.95 / 2
Cyclomatic Complexity 1.13 / 3 1.23 / 3
Lines Of Code Per Method 5.17 / 12 6.29 / 9
Num. of Methods Per Type 5.25 / 11 5.00 / 9
Number of Parameters 0.23 / 2 0.40 / 2
Efferent Couplings 2 2
Lines of Code 143 130
Number of Methods 21 15
Test cases 10 (1 de Cell) 8

Segn las mtricas de profundidad de bloque y la complejidad ciclomtica,
ambas soluciones presentan una complejidad similar, aunque la segunda solucin tiene
una complejidad ciclomtica ligeramente mayor.
En cuanto al tamao la segunda solucin tiene menos lneas de cdigo y menos
mtodos, a pesar de haber refactorizado ms que la primera solucin. Los mtodos de la
segunda tienen ms cdigo que la primera con una media de 6,29, pero dicho cdigo
est distribuido de manera ms homogneo ya que el mtodo ms largo de la segunda
solucin slo tiene 9 lneas por 12 lneas de la primera solucin
Respecto al acoplamiento, ambas soluciones presentan valores similares y
bastante bajos.
Estos resultados reflejan que utilizar una clase para encapsular la lgica de
gestionar cada uno de los pozos, como la clase Cell de la primera solucin, no supone
una mejora apreciable en la solucin final.
Para reflexionar
Un detalle a tener en cuenta es que no hemos escrito ningn comentario en el cdigo, ni
en los casos de prueba ni en el cdigo de produccin. Los has echado de menos?
Crees que hubieran sido necesarios? Si crees que, en el futuro, te costara entender
alguna arte del cdigo de esta kata piensa cmo mejorar su legibilidad con buen
refactorizacin, o con una prueba adicional, antes que con un comentario.
Uno de los principales errores a la hora de aplicar TDD y que hemos cometido en
la primera solucin es querer ir demasiado rpido escribiendo el cdigo. A veces
tenemos el cdigo en la cabeza y queremos volcarlo rpidamente en el entorno de
desarrollo. Pero nuestro cerebro no es un compilador y puede tener cdigo que no
68

funcione y que sea difcil y costoso hacerlo funcionar. Al aplicar TDD de manera ms
estricta en la segunda solucin hemos escrito menos cdigo. TDD nos lleva a buscar la
solucin ms sencilla posible, por lo que escribimos menos cdigo.
Conclusiones
En este captulo hemos visto dos soluciones distintas a una misma kata. Hemos tenido el
coraje para descartar nuestro cdigo y volver a empezar y, como resultado, hemos
obtenido una solucin mejor: ms simple y ms pequea.
Aunque hemos completado el enunciado que planteamos al principio del
captulo, el juego del Mancala no est completo. A continuacin tienes una lista de la
funcionalidad que falta por implementar para completarlo.
Si a la hora de sembrar se da una vuelta completa al Mancala, se omite el pozo
de origen. Es decir, despus de una siembra el pozo de origen est vaco,
independientemente de las vueltas que hayamos dado al Mancala sembrando.
El juego termina cuando el jugador al que le toca mover no puede por no tener
semillas.
Cuando el juego termina porque un jugador no puede mover, el otro jugador
coge todas las semillas de sus pozos y las guarda en su choza.
El ganador es el jugador con ms semillas en su choza.
Agradecimientos del captulo
A Antonio Martnez por inspirar la idea para este captulo y por sus revisiones de
los primeros borradores.

69




Captulo 5.
Ejercicio TDD para presentar y usar
mocks / doubles, etc.





70



Captulo 6.
Cifrado Esctala y Refactoriacin



La legibilidad del cdigo es un aspecto clave en TDD ya que vamos aadiendo nuevo
cdigo de manera incremental. Para mejorar la legibilidad aplicamos refactorizaciones
despus de escribir el mnimo cdigo que pasa con xito una prueba.
Este captulo tiene una orientacin diferente a la de captulos anterior. Ahora,
no empezaremos desde cero sino que partiremos de una solucin al problema que,
aunque correcta, presenta un buen nmero de malos olores que reautorizaremos.
El Problema
El cifrado Esctala es una tcnica utilizada en la antigua Grecia para comunicar mensajes
secretos. Para implementarla, el emisor coga una tira de cuero, la enrollaban alrededor
de una vara y escriban el mensaje en ella a lo largo de la vara.
El receptor del mensaje deba tener una vara de grosor similar para enrollar
nuevamente el mensaje y poder leerlo.
El Cdigo
A continuacin puedes ver una implementacin de la codificacin y decodificacin
utilizando la tcnica de la Exctala.


71

Cdigo 6.1

public class Escitala {
private int caras;
private String frase;
private String[][] escitala;
private int largo;

public Escitala(int caras, String frase) {
this.caras = caras;
this.frase = frase;
}

public String encrypt() {
if (isValid()) {//comprobamos la frase las caras etc
largo = (frase.length() % caras) == 0 ? frase.length() / caras:
frase.length() / caras + 1; //Si el mensaje dividido entre las caras de la
escitala da resto cero no sobraran espacios
escitala = new String[caras][largo]; //las caras representan las
columnas de la escitala
//el largo representa las filas

int pivote = 0; //nos servir de pivote para sealar el caracter
actual en la frase

for (int columna = 0; columna < largo; columna++) { //recorremos todas
las filas DE CADA COLUMNA
for (int fila = 0; fila < caras; fila++) { //recorremos todas las
columnas
if (pivote < frase.length()) { //Si no hemos recorrido toda
la frase
escitala[fila][columna] =
String.valueOf(frase.charAt(pivote)); //Almacenamos el caracter que toque dentro
del Array
pivote++; //sumamos uno al pivote
para apuntar al siguiente caracter de la cadena de texto a encrypt
} else //Si hemos llegado al
final de la frase pero an quedan datos que cumplimentar en el array
escitala[fila][columna] = aleatorio(); //como ya no
quedan letras de la frase generamos un caracter aleatorio para cumplimentar los
elementos del array
}
}
return toText(caras, largo); //llamamos con la cantidad de filas
}
return null;
}

public String decrypt(String frase2) {
if (isValid()) { //comprobamos la frase las caras etc
largo = (frase.length() % caras) == 0 ? frase.length() / caras:
frase.length() / caras + 1; //Si el mensaje dividido entre las caras de la
escitala da resto cero no sobraran espacios
escitala = new String[largo][caras];

int pivote = 0; //Nos servir de pivote para la frase

for (int columnas = 0; columnas < caras; columnas++) { //OJO esta vez
la cantidad de columnas lo determinarn las caras
for (int filas = 0; filas < largo; filas++) { //OJO esta vez las
la cantidad de filas vendr determinada por el largo.
escitala[filas][columnas] =
String.valueOf(frase2.charAt(pivote)); //vamos escribiendo caracter a caracter en
el array bidimensional
pivote++; //avanzamos el pivote una posicin para que
apunte al siguiente caracter del String a convertir.
}
}
return toText(largo, caras); //llamamos con la cantidad de filas,
columnas. OJO hay que pasar como parmetro la variable que indique el mximo de
filas y luego la
72

} //que indique el mximo de columnas.
return null;
}

private String aleatorio() {
int aleatorio = (int) (Math.random() * 100) + 1; //calculamos un
aleatorio de 1 a 100. Podramos haber pusto otro rango.

return String.valueOf((char) aleatorio); //Devolvemos como String el char
que representa a ese nmero generado aleatorimente. (Cdigo ASCII)
}

private String toText(int f, int c) {
StringBuilder sb = new StringBuilder(); //Ya que no trabajamos con hilos
creamos un StringBuilder que es ms eficiente en estos casos

for (int filas = 0; filas < f; filas ++) { //Recorremos el
array
for (int columnas = 0; columnas < c; columnas++) {
sb.append(escitala[filas][columnas]); //y vamos agregando al
StringBuilder caracter a carater
}
}
return sb.toString(); //Devolvemos como String el contenido del
StringBuilder
}

private boolean isValid() {
boolean correcto = false;

if (caras > 0 && frase != null && frase.length() > caras) {
correcto = true;
} else
System.out.println("Las cara deben ser un nmero entero positivo
comprendido y la frase no puede ser nula. \n" +
".Adems la cantidad de letras de la frase no puede ser menor
al nmero de caras");
return correcto;
}

public int getCaras() {
return caras;
}

//MTODOS GETTERS AND SETTERS TPICOS.
public void setCaras(int caras) {
if (caras > 0)
this.caras = caras;
}

public String getFrase() {
return frase;
}

public void setFrase(String frase) {
this.frase = frase;
}
}

Vamos a trabajar con el cdigo anterior a lo largo de este captulo.
Podemos leerlo?
Es difcil trabajar con un cdigo difcil de leer y el cdigo anterior lo es. Las lneas son
muy largas y, adems los comentarios se incluyen en la misma lnea. Hay un nmero
excesivo de comentarios que no facilitan, sino dificultan entender el cdigo.
73

El primer paso que daremos ser eliminar comentarios superfluos (como
recorremos todas las columnas) y reorganizar el formato cdigo para que sea fcil de
leer. Haremos eso con cuidado, ya que an no tenemos pruebas que nos adviertan si
hemos introducido algn error.
Cdigo 6.2
package escitala;

public class Escitala {
private int caras;
private String frase;
private String[][] escitala;
private int largo;

public Escitala(int caras, String frase) {
this.caras = caras;
this.frase = frase;
}

public String encrypt() {
if (isValid()) {
//Si el mensaje dividido entre las caras de
// la escitala
// da resto cero no sobraran espacios
largo = (frase.length() % caras) == 0 ?
frase.length() / caras:
frase.length() / caras + 1;
//las caras representan las columnas de
// la escitala
//el largo representa las filas
escitala = new String[caras][largo];

// pivote para sealar el caracter actual
// en la frase
int pivote = 0;

for (int columna = 0; columna < largo; columna++) {
for (int fila = 0; fila < caras; fila++) {
if (pivote < frase.length()) {
escitala[fila][columna] =

String.valueOf(frase.charAt(pivote));
pivote++;
} else
//como ya no quedan letras de
//la frase generamos un caracter
// aleatorio para cumplimentar
//los elementos del array
escitala[fila][columna] =
aleatorio();
}
}
return toText(caras, largo);
}
return null;
}
74


public String decrypt(String frase2) {
if (isValid()) {
largo = (frase.length() % caras) == 0 ?
frase.length() / caras:
frase.length() / caras + 1;
escitala = new String[largo][caras];

int pivote = 0;

//la cantidad de columnas lo determinarn las caras
for (int columnas = 0; columnas < caras; columnas++)
{
for (int filas = 0; filas < largo; filas++) {
escitala[filas][columnas] =

String.valueOf(frase2.charAt(pivote));
pivote++;
}
}
return toText(largo, caras);
}
return null;
}

private String aleatorio() {
int aleatorio = (int) (Math.random() * 100) + 1;

return String.valueOf((char) aleatorio);
}

private String toText(int f, int c) {
StringBuilder sb = new StringBuilder();

for (int filas = 0; filas < f; filas ++) {
for (int columnas = 0; columnas < c; columnas++) {
sb.append(escitala[filas][columnas]);
}
}
return sb.toString();
}

private boolean isValid() {
boolean correcto = false;

if (caras > 0 && frase != null && frase.length() > caras) {
correcto = true;
} else
System.out.println("Las cara deben ser un nmero " +
"entero positivo " +
"comprendido y la frase no puede ser nula. \n"
+
".Adems la cantidad de letras de la frase no
" +
"puede ser " +
"menor al nmero de caras");
return correcto;
75

}

public int getCaras() {
return caras;
}

public void setCaras(int caras) {
if (caras > 0)
this.caras = caras;
}

public String getFrase() {
return frase;
}

public void setFrase(String frase) {
this.frase = frase;
}
}

En el cdigo 6.2 hay ms lneas de cdigo de las que tenamos en el 6.1. A
cambio, hemos mejorado su legibilidad. Ahora podemos hacer una lista de malos olores.
Por ejemplo mensajes de texto incrustados en el texto o bloques de cdigo sin llaves o
Cdigo duplicado. Tambin podemos ver, leyendo los comentarios, que la mayora de
los nombres parecen inadecuados, ya que hace falta explicar qu hace cada variable,
cuando eso debera quedar claro solo por su nombre. Volveremos sobre esto ms
adelante, porque, sin pruebas, es mejor no modificar el cdigo.
Vamos a crear nuestro diario de diseo para identificar las mejores que vamos a
aplicar al cdigo.
Cuadro 6.3 Diario de diseo
Escribir pruebas.
Evitar el cdigo duplicado en los mtodos encript y decript.
Quitar el mensaje del constructor y aadirlo al mtodo encript
Quitar los mtodos get / set no utilizados
Nombres ms descriptivos para variables y mtodos
Quitar mensajes.
Todos los bloques de cdigo entre llaves.

En los prximos apartados veremos cmo corregir estos malos olores, peor
antes vamos a escribir un conjunto de pruebas.
Escribiendo Pruebas
Por qu tenemos que escribir pruebas si damos por hecho que el cdigo funciona? Las
pruebas sern nuestra garanta de que no introducimos errores a la hora de cambiar el
cdigo. Asumimos que el cdigo funciona, por lo que comenzamos escribiendo pruebas
de aceptacin. Una de ellas (utilizando la sintaxis Gerkin) se muestra en el cdigo 6.4
76

Cdigo 6.4. Escenario.

La implementacin de esta prueba se muestra en el cdigo 6.5.
Cdigo 6.5

Si esta prueba diera error, tendramos que escribir pruebas para los mtodos
auxiliares buscando acotar el fragmento de cdigo dnde est el error. En este caso, la
prueba 6.5 funciona correctamente, por lo que pasamos a la siguiente prueba.
Vamos a escribir una prueba para el mtodo de desencriptar. Esta prueba es la
misma que la anterior cambiando los mensajes. La implementacin est en el cdigo
6.6.
Cdigo 6.6

Esta prueba tambin funciona, por lo que es el momento de empezar a hacer
cambios.
Cuadro 6.7. Diario de diseo
Escribir pruebas.
Evitar el cdigo duplicado en los mtodos encript y decript.
Quitar el mensaje del constructor y aadirlo al mtodo encript.
77

Sacar los mtodos encript y decript del bloque if.
Quitar los mtodos get / set no utilizados
Nombres ms descriptivos para variables y mtodos
Quitar mensajes de texto.
Todos los bloques de cdigo entre llaves.
Excepciones en vez de resultados nulos.

Vamos a actualizar el diario de diseo con los cambios que iremos haciendo a
continuacin (cuadro 6.7).
Desbloqueando a los mtodos principales.
Actualmente el cdigo de los mtodos est escondido en un bloque if (cdigo 6.8)
Cdigo 6.8.


Tener todo el cuerpo de un mtodo dentro de if lo hace ms difcil de leer y
entender, por lo que vamos a cambiar la condicin del if para sacar el cdigo fuera del
bloque. El mtodo encript quedara como se muestra en el cdigo 6.9 y le mtodo
decript quedara como se muestra en el cdigo 6.10.




78

Cdigo 6.9.


Ejecutamos las pruebas para comprobar que no hemos introducido ningn error y ya
podemos tachar esta tarea en nuestro diario de diseo y continuar con la siguiente
tarea.
Cuadro 6.10. Diario de diseo
Escribir pruebas.
Evitar el cdigo duplicado en los mtodos encript y decript.
Quitar el mensaje del constructor y aadirlo al mtodo encript.
Sacar los mtodos encript y decript del bloque if.
Quitar los mtodos get / set no utilizados
Nombres ms descriptivos para variables y mtodos
Quitar mensajes de texto.
Todos los bloques de cdigo entre llaves.
Excepciones en vez de resultados nulos.


79

Cambiando mensajes por excepciones
El mensaje de texto del cdigo actual nos indica que el nmero de caras no es vlido. Sin
embargo este cdigo es un componente de otro cdigo por lo que no debe mostrar
mensajes directamente. La responsable de enviar mensajes ser la interfaz de usuario.
Vamos a cambiar el mensaje por una excepcin. El primer paso ser escribir una
prueba que indique el objetivo que buscamos (cdigo 6.11).
Cdigo 6.11. Prueba.


La prueba no se puede ejecutar porque nos falta la clase excepcin. Esta clase
podemos crearla de manera automtica con los asistentes de los IDEs ms habituales.
Un ejemplo, generado con Eclipse se muestra en el cdigo 6.12.
Cdigo 6.12. Clase excepcin generada con Eclipse.


Ahora la prueba falla porque, cuando detecta el error, no se lanza la excepcin.
Vamos a cambiar eso en el cdigo. Vamos a hacer que la excepcin la lance el mtodo
80

pblico para no dar detalles de mtodos privados. La modificacin se muestra en el
cdigo 6.13.
Cdigo 6.13. Lanza la excepcin en lugar de devolver nulo


Ahora, vamos a implementar un mensaje descriptivo en la excepcin. De tal
manera que, cuando se lance, el mensaje nos indique qu falla y cmo podemos
corregirlo. Para ello, empezamos escribiendo una prueba (cdigo 6.14) que nos permita
definir la informacin del mensaje.
Cdigo 6.14. Lanza la excepcin en lugar de devolver nulo


La prueba anterior, aunque breve, es difcil de entender. Al final del captulo la
refactorizaremos. El primer paso para implementar esta prueba es crear un constructor
que admita un entero. Esa modificacin se muestra en el cdigo 6.15.
Cdigo 6.15. Constructor con el nmero de caras


81

Hemos quitado el resto de constructores del cdigo 6.12 para que siempre que
se cree una excepcin se indique el nmero de caras. Vamos a incluir ahora el mensaje
y, para ello, aprovechamos el mensaje del cdigo original. El resultado est en el cdigo
6.16.
Cdigo 6.16.


Con el cdigo anterior, la prueba del cdigo 6.14 pasa con xito, pero el cdigo
de la clase Esctala da un error, ya que hemos quitado constructores. Para ello hacemos
aadimos el nmero de caras en el momento de lanzar la excepcin (cdigo 6.17) y
comprobamos que todo vuelve a funcionar.

Cdigo 6.17.


Para terminar vamos a refactorizar el mtodo isValid para que ya no muestre el
mensaje de error. Adems, cambiaremos el nombre de la funcin para que sea ms
claro qu se comprueba y cuando es vlido. El resultado se muestra en el cdigo 6.18.
Cdigo 6.18.


Por ltimo ejecutamos todas las pruebas para verificar que el cdigo sigue
funcionando.
82


An quedan ms cosas que contar en este captulo.

Conclusiones

Es errneo pensar que cuanto ms comentarios pongamos a nuestro cdigo ms fcil
ser de entender. Ahora t mismo puedes comprobarlo comparando el cdigo del
principio de este captulo con el resultado final. Qu cdigo entiendes mejor?
Tambin puedes comprobar lo tiles que nos han resultado las pruebas que escribimos
al principio ya que, junto a los ciclos TDD hechos para cada cambio, nos han dado la
seguridad de que no estbamos introduciendo nuevos errores. Despus de cada cambio,
el cdigo ha seguido encriptando y desencriptando correctamente.
Slo hemos escrito pruebas para los mtodos pblicos y no para los mtodos privados.
Vamos a ver una breve justificacin. Los mtodos privados con internos de la clase por
lo que no deberan ser probados, ni tampoco contener una lgica demasiado compleja.
Adems Java no ayuda a la hora de probar mtodos privados ya que hay que dar rodeos
(introspeccin, herencia y delegacin, etc.) para poder acceder a ellos.
En el ejemplo que hemos probado los mtodos privados a travs de los mtodos
pblicos. Un fallo en un mtodo privado desencadena que no se encripte o desencripte
correctamente.

Agradecimientos del captulo

Al usuario Sie7e de Solveet (www.solveet.com) por desarrollar la implementacin
original que hemos utilizado de base en este captulo.




83




Captulo 7.
Suscriptores y Dobles de Prueba



En este captulo vamos a plantear un problema que implementaremos utilizando dobles
de prueba con la librera Mockito. Si quieres conocer cmo funciona Mockito y ver
ejemplos que te servirn para entender el cdigo de este captulo puede ir a los anexos
de este libro.
El problema
Tenemos una estructura de datos, por ejemplo, una cola con los mtodos del cdigo 7.1.
Modificaremos esta cola para que acepte subscriptores que sern avisados cuando se
aada o borre un nuevo elemento a la cola.
Cdigo 7.1. API de la cola
class Queue<T> {
public Queue() {}
public void add(T e) {}
public T remove(){}
public Iterator<T> iterator(){}
public int size(){}
}

Un subscriptor es cualquier clase que implemente una interfaz concreta (que
definiremos a medida que vayamos realizando la implementacin con TDD). La propia
84

cola almacenar todos los subscriptores que tiene. Por simplicidad no tendremos en
cuenta la posibilidad de dar de baja a un subscriptor.

El API de Java, desde la versin 1.0 incluye dos interfaces para implementar el patrn
observador / observable cuya misin es la misma que el mecanismo de subscripcin
que vamos a crear. Estas interfaces son java.util.Observable y java.util.Observer.


Veamos algunos ejemplos de cmo funciona este mecanismo de subscripcin en
la tabla 7.1.
Tabla 7.2. Funcionamiento de la subscripcin a la cola.
Escenario Resultado esperado
Se aade un nuevo elemento Se notifica a todos los suscriptores que se ha
aadido el elemento junto con el propio
elemento.
Se borra un elemento Se notifica a todos los suscriptores que se ha
borrado el elemento junto con el propio
elemento.
Se pide borrar un elemento pero la
cola est vaca
No se notifica nada a los subscriptores.
Cualquier otra operacin de la cola Nos e notifica nada a los subscriptores.

A partir de la informacin y los ejemplos anteriores vamos a crear nuestro diario
de diseo (cuadro 7.1).
7.1. Diario de diseo. Primera versin.
Permitir a un objeto subscribirse a la cola (clase Queue).
Enviar una notificacin cuando se aada un elemento.
Enviar una notificacin cuando se borre un elemento.


Este problema es muy adecuado para aplicar un doble de prueba. El resultado
esperado de la funcionalidad de subscripcin no es un resultado que podamos verificar
con un assert. En este caso el resultado esperado, y lo tendremos que comprobar para
decidir si la prueba pasa o falla es que el mtodo correcto se haya llamado con los
parmetros adecuados.
Vamos a comenzar a implementar la funcionalidad de suscripcin eligiendo el
caso ms sencillo.
85

La primera prueba de subscripcin
Elegimos empezar implementando todo lo necesario para que un objeto pueda
subscribirse a la cola y reciba la notificacin correspondiente cuando se aada un nuevo
elemento. Comenzamos escribiendo la prueba del cdigo 7.2.
Cdigo 7.2. Primera prueba.
@Test
public void
whenAnelementIsAddedOneSubscriptorReceivesACallInMethod_newEvent_() {
Subscriber sub = mock(Subscriber.class);

Queue<String> q = new Queue<String>();
q.subscribe(sub);

q.add("new string");

verify(sub).newEvent();
}

Analicemos esta prueba en detalle para ver qu decisiones hemos tomado y qu
decisiones estamos posponiendo para el futuro.
La primera decisin que hemos tomado ha sido definir el nombre de la interfaz
Subscriber. Esta interfaz, que an no existe, la implementarn todos aquellos objetos
que se subscriben a la cola. Adems, hemos utilizado la herramienta Mockito para crear
un objeto subscriptor sin necesidad de crear una clase que implemente la interfaz.
La segunda decisin que hemos tomado ha sido permitir aadir subscriptores a
la cola mediante el mtodo subscribe, que tampoco existe an.
La tercera, y ltima decisin que hemos tomado al escribir esta prueba es que
los suscriptores de una cola van a recibir una llamada al mtodo Subscriber::newEvent
cuando se aada un nuevo elemento.
Hemos intentado no tomar demasiadas decisiones ahora y posponerlas para el
futuro. Por ejemplo, no definimos an qu parmetros acompaaran al mtodo
newEvent.

Intenta empezar por el caso ms sencillo y posponer todas las decisiones que puedas.
Cuanto ms tardes en tomar una decisin ms informacin tendrs para decidir. Una
decisin tomada pronto puede ser una piedra colgando del cuello que vayas
arrastrando todo el proyecto.


86

Parece que estamos rompiendo la regla de avanzar con pequeos pasos (o
babysteps) y estamos abarcando mucha funcionalidad en esta prueba. Sin embargo hay
un buen motivo para hacerlo.
Vamos a suponer que comenzamos implementando todo lo necesario para la
subscripcin. Cmo podramos probarlo si no hemos definido una manera de aadir
suscriptores? Supongamos tambin que comenzamos implementando slo el
mecanismo de suscripcin. Cmo podramos verificar que la subscricin se ha realizado
correctamente?
No hay necesidad de aadir ningn mecanismo pblico. Aunque podramos
poner algn mtodo que nos permitiera obtener el estado de las suscripciones, por
ejemplo un mtodo getSubscriptires o un mtodo toString, no es necesario para lo que
queremos implementar.
Las suscripciones por s mismas no tiene valor, el valor los tienen las
notificaciones. Las subscripciones son un mecanismo necesario para realizar las
notificaciones. Por este motivo, nuestra primera prueba involucra las notificaciones.
Para implementar la prueba del cdigo 7.2 tenemos que dar varios pasos, pero
todos pequeos y automticos en la mayora de IDEs. El primero de ellos es crear la
interfaz Subscriber y el mtodo newEvent (cdigo 7.3).
Cdigo 7.3. Primera implementacin de la interfaz
public interface Subscriber {

void newEvent();

}

Despus, implementamos el mnimo cdigo posible en la clase Queue para hacer
que nuestra prueba se ejecute. Primero optamos por un fake para implementar el
mecanismo para aadir un subscriptor (cdigo 7.4).
Cdigo 7.4. Subscriptores en la cola

Subscriber sub;

public void subscribe(Subscriber sub) {
this.sub = sub;
}


El ltimo paso es modificar el mtodo add de la cola para que realice la
notificacin. Tambin optamos por un fake para triangular ms adelante (cdigo 7.5).

87

Cdigo 7.5. Notificacin en el mtodo Queue::add

public void add(T e) {
// ... Cdigo original ...
this.sub.newEvent();
}


Con todo lo anterior, la prueba funciona. En total hemos escrito 6 lneas de
cdigo (dos para declarar la interfaz, dos para el mtodo subscribe, una para el atributo
sub y otra en el mtodo add) sencilla, por lo que hemos hecho un buen baystep.
Vamos a actualizar el diario de diseo con todo lo que hemos aprendido en esta
primera prueba. Las decisiones que hemos propuesto las anotamos como tareas para
hacer (cuadro 7.2).
7.2. Diario de diseo despus de la primera prueba
Permitir a un objeto subscribirse a la cola (clase Queue).
Enviar una notificacin cuando se aada un elemento.
Permitir subscripciones de varios objetos
Enviar la notificacin de aadir a todos los objetos subscritor
Notificar el tipo de evento
Notificar el elemento aadido o borrado
Enviar una notificacin cuando se borre un elemento.


Aunque podramos refactorizar algunas partes del cdigo esperando futuras
modificaciones, no lo vamos a hacer an, pero lo haremos pronto. Ahora podemos
continuar mejorando los parmetros o bien podemos hacer que se avise a todos los
suscriptores. Vamos a hacer esto segundo con la siguiente prueba.
Dos son multitud
Vamos a evolucionar nuestro cdigo para que admita ms de un subscriptores. Para ello
elegimos esa tarea de nuestro diario de diseo y escribimos una prueba que refleje lo
que queremos conseguir y que nos permita triangular el cdigo de la cola.
Tal y como tenemos organizado el cdigo no tiene mucho sentido considerar
slo la posibilidad de aadir ms de un subscriptor sin incluir tambin la verificacin de
que las notificaciones llegan a todos los subscriptores, por lo que implementamos
ambas tareas en la siguiente prueba (cdigo 7.6).



88

Cdigo 7.6. Prueba con dos subscriptores
@Test
public void
testWhenAnelementIsAddedTwoSubscriptorsReceivesACallInMethod_newEvent_() {
Subscriber sub01 = mock(Subscriber.class);
Subscriber sub02 = mock(Subscriber.class);

Queue<String> q = new Queue<String>();
q.subscribe(sub01);
q.subscribe(sub02);

q.add("new string");

verify(sub01).newEvent();
verify(sub02).newEvent();

}

Realmente esta prueba es muy sencilla, basta con en duplicar el cdigo de la
primera prueba que escribimos (cdigo 7.2). Esto es un mal olor que refactorizaremos
cuando ambas pruebas funcionen correctamente.
Para implementar esta prueba, tenemos que completar el cdigo de Queue que
escribimos en la prueba anterior. Primero substituimos el tipo del atributo sub por una
lista de subscriptores. Despus el mtodo subscribe aade el nuevo suscriptor a la lista.
Por ltimo, cuando se aada un nuevo elemento a la cola, recorreremos la lista de
suscriptores llamando al mtodo newEvent de cada uno. Estos cambios estn recogidos
en el cdigo 7.7
Cdigo 7.7. Queue soporta ms de un subscriptor
List<Subscriber> subs = new ArrayList<Subscriber>();

public void subscribe(Subscriber sub) {
this.subs.add(sub);
}

public void add(T e) {
// Cdigo anterior

for (Subscriber sub: this.subs) {
sub.newEvent();
}
}

Con el cdigo anterior, ambas pruebas funcionan correctamente. Es el momento
de refactorizar para quitar malos olores.
89

Refactorizando pruebas repetidas
La segunda prueba que hemos escrito en el cdigo 7.6, hace que la primera prueba que
(cdigo 7.32) no sea necesaria, ya que si esta segunda prueba funciona correctamente la
primera tambin funcionar.
S nos ha sido til escribir ambas pruebas porque ambas nos han permitido que
nuestro cdigo progrese y crezca pero ahora no hay necesidad de mantener ambas, por
lo que podemos borramos la primera prueba.
Tambin vamos a refactorizar la prueba. Sabemos, porque lo tenemos en el
diario de diseo del cuadro 7.2, que vamos a modificar el mtodo newEvent de la
interfaz subscriptor, por tanto nos interesa aislar todo lo que tenga que ver con este
mtodo para hacer las modificaciones en un nico punto y no tener que hacer cambios
por todo el cdigo.
Si nos fijamos en la prueba del cdigo 7.6 vemos que se comprueba la llamada al
mtodo 2 veces. Cuando cambien los parmetros, esta comprobacin tambin
cambiar, as que vamos a refactorizar para aislar esta comprobacin en un nico punto.
El resultado se muestra en el cdigo 7.8.
Cdigo 7.8.
@Test
public void
testWhenAnelementIsAddedTwoSubscriptorsReceivesACallInMethod_newEvent_() {
Subscriber sub01 = mock(Subscriber.class);
Subscriber sub02 = mock(Subscriber.class);

Queue<String> q = new Queue<String>();
q.subscribe(sub01);
q.subscribe(sub02);

q.add("new string");

verifyNewEventIsCalled(sub01);
verifyNewEventIsCalled (sub02);
}

private void verifyNewEventIsCalled (Subscriber sub) {
verify(sub).newEvent();
}


Ahora, cuando el mtodo newEvent cambie solo tendremos que cambiar una
lnea en el mtodo verifyNewEventIsCalled.
Adems, vamos a hacer una rpida refactorizacin de la clase Queue para
inicializar el nuevo atributo en el constructor, en lugar de en la declaracin. El resultado
se muestra en el cdigo 7.9.
90

Cdigo 7.9. Refatorizacin de la creacin de la lista de subscriptores.

List<Subscriber> subs;

public Queue() {
//...
subs = new ArrayList<Subscriber>();
}


Vamos a retomar nuestro diario de diseo y a continuar aadiendo pruebas y
funcionalidad.
Completando las subscripciones
Veamos el diario de diseo para saber qu nos queda por hacer y elegir la prxima
caracterstica a implementar (cuadro 7.3).
7.3. Diario de diseo despus de la primera prueba
Permitir a un objeto subscribirse a la cola (clase Queue).
Enviar una notificacin cuando se aada un elemento.
Permitir subscripciones de varios objetos
Enviar la notificacin de aadir a todos los objetos subscritor
Notificar el tipo de evento
Notificar el elemento aadido o borrado
Enviar una notificacin cuando se borre un elemento.


De entre todas las tareas an nos quedan, elegimos incluir el tipo de evento en
la notificacin. Como solo tenemos un mtodo para ambas operaciones, el mtodo
newEvent. Vamos a incluir el tipo de evento, si se aade o borra elemento, y dejaremos
la notificacin del objeto aadido o borrado para ms adelante, y as podemos avanzar
en babysteps y tenemos la oportunidad de ir refactorizando nuestro cdigo. La prueba
que define lo que queremos obtener se muestra en el cdigo 7.10.
Cdigo 7.10. Prueba que verifica el tipo del evento.
@Test
public void testWhenAnElementIsAddedSubsCritorReceives_Add_Event() {
Subscriber sub = mock(Subscriber.class);

Queue<String> q = new Queue<String>();
q.subscribe(sub);

q.add("new string");

verify(sub).newEvent(Queue.Event.Add);
}
91


En la prueba del cdigo 7.10 hemos decidido que cada evento se clasifica en
base al enumerado Event adems, este enumerado se define dentro de la clase Queue.
El mtodo newEvent recibe cmo parmetro el tipo de evento. En la prueba verificamos
que se llama al mtodo newEvent del subscriptor y, adems, el primer parmetro es el
evento Add.
De nuevo tenemos que escribir cdigo en distintos lugares para implementar
esta prueba. En primer lugar implementamos el cdigo del enumerado Event en el
cdigo 7.11.
Cdigo 7.11. Enumerado con los tipos de eventos.

public class Queue<T> {
public static enum Event {Add};
// ..
}

Despus, modificamos el mtodo add para pasar por parmetro el tipo de
evento a cada subscriptor. En este caso utilizamos la implementacin obvia ya que el
mtodo add siempre usar el mismo tipo de evento (cdigo 7.12).
Cdigo 7.12. Enumerado con los tipos de eventos.

public void add(T e) {
//...
for (Subscriber sub: this.subs) {
sub.newEvent(Event.Add);
}
}


Adems, al aadir un parmetro al mtodo newEvent es necesario modificar a
interfaz subscribe de la siguiente manera. Esta modificacin est en el cdigo 7.13.
Cdigo 7.13. Modificacin de la interface Subscriber.

public interface Subscriber {
void newEvent(Event event);
}

Cuando trabajamos con dobles de prueba podemos hacer evolucionar la
interfaces que utilizamos para crear los dobles. Con ello vamos adaptando los servicios
que simulan los dobles a nuestras necesidades. Al final, tendremos una interfaz que se
ajustar con bastante precisin a lo que queremos.
Observa tambin que, gracias a trabajar con dobles de prueba, un cambio en la
interfaz no implica tener que cambiar ninguna clase, ya que los dobles de prueba
generan la implementacin al vuelo, lo que hace ms gil la escritura de pruebas.
92

Con los cambios anteriores la nueva prueba funciona, pero la prueba anterior
(llamada
testWhenAnelementIsAddedTwoSubscriptorsReceivesACallInMethod_newEvent_) falla
porque espera llamadas al mtodo newEvent sin parmetro y ahora se recibe un
parmetro.
Si nos fijamos en el cdigo de ambas pruebas vemos que es el mismo y que
ambas pruebas verifican la misma condicin, esto es, la llamada correcta a newEvent.
Por tanto podemos prescindir de la prueba que hemos creado y modificar la anterior.
Los cambios se muestran en el cdigo 7.14.
Cdigo 7.14.

private void verifyNewElementIsCalled(Subscriber sub) {
verify(sub).newEvent(any(Queue.Event.class));
}

Lo que ha pasado aqu es normal. Tenemos una prueba que verifica la llamada
de un mtodo y ese mtodo cambia, por tanto la prueba tambin va a cambiando a la
misma vez y no es necesario escribir pruebas nuevas. Esto aparecer de nuevo ms
adelante cuando aadamos el elemento (aadido o borrado) al mtodo newEvent.
Refactorizando las pruebas
Recuerda que las pruebas son tan importantes como el cdigo de produccin y que
tambin debemos refactorizarlas para que sean fciles de entender y modificar. Aunque
slo tenemos una nica prueba, tenemos que modificarla a medida que vamos
completando el mtodo newEvent, por lo que es importante que la prueba sea fcil de
entender. En el cdigo 7.15 tienes toda la clase de pruebas tal y como est ahora mismo.
Cdigo 7.15.
public class TestSubscription {

@Test
public void whenElementIsAdded2SubsReceivesACallIn_newEvent() {
Subscriber sub01 = mock(Subscriber.class);
Subscriber sub02 = mock(Subscriber.class);
Queue<String> q = new Queue<String>();
q.subscribe(sub01);
q.subscribe(sub02);

q.add("new string");

verifyNewElementIsCalled(sub01);
verifyNewElementIsCalled(sub02);
}

private void verifyNewElementIsCalled(Subscriber sub) {
verify(sub).newEvent(any(Queue.Event.class));
}
}

93


Si has ido siguiendo el resto de captulos de este libro vers fcilmente qu
refactorizaciones podemos hacer. El resultado lo tienes en el cdigo 7.16.
Cdigo 7.16.

public class TestSubscription {

Subscriber sub01, sub02;
Queue<String> queueWithTwoSubscribers;

@Before
public void setUp() {
sub01 = mock(Subscriber.class);
sub02 = mock(Subscriber.class);
queueWithTwoSubscribers = new Queue<String>();
queueWithTwoSubscribers.subscribe(sub01);
queueWithTwoSubscribers.subscribe(sub02);
}

@Test
public void
testWhenAnelementIsAddedTwoSubscriptorsReceivesACallInMethod_newEvent_()
{
queueWithTwoSubscribers.add("new string");

verifyNewElementIsCalled(sub01);
verifyNewElementIsCalled(sub02);
}

private void verifyNewElementIsCalled(Subscriber sub) {
verify(sub).newEvent(any(Queue.Event.class));
}



Hemos refactorizado el cdigo que crea la cola y los dobles de prueba para tener
menos cdigo en la prueba y que esta se centre en la accin y las verificaciones, sin
tener que incluir detalles de cmo crean los objetos que participan (ahora se crean en el
mtodo setUp). Adems hemos cambiado algunos nombres para que transmitan con
ms claridad qu son, por ejemplo ahora el objeto cola se llama cola con dos
subscriptores.
An podramos hacer alguna refactorizacin ms al cdigo de prueba, pero la
dejaremos para ms adelante. No vemos ningn mal olor lo suficientemente fuerte en la
cola como para refactorizar por lo que seguimos adelante.
Un nuevo husped
El estado de nuestro diario de diseo es el siguiente (cuadro 3.4).

94

3.4. Diario de diseo despus de la primera prueba.
Permitir a un objeto subscribirse a la cola (clase Queue).
Enviar una notificacin cuando se aada un elemento.
Permitir subscripciones de varios objetos
Enviar la notificacin de aadir a todos los objetos subscritor
Notificar el tipo de evento
Notificar el elemento aadido o borrado
Enviar una notificacin cuando se borre un elemento.


Vamos a completar el mtodo newEvent implementando la notificacin del
elemento aadido. Como ya tenemos escrita una prueba que verifica la llanada a
newEvent despus de aadir un elemento, no tiene sentido escribir una nueva prueba.
En su lugar cambiamos la verificacin de la prueba que tenemos ahora mismo (cdigo
7.17).
Cdigo 7.17.
private void verifyNewElementIsCalled(Subscriber<String> sub) {
verify(sub).newEvent(any(Queue.Event.class), any(String.class));

}

El cdigo 7.17 verifica que el mtodo newEvent ha sido llamado con cualquier
evento y con una cadena de texto concreta, la misma cadena de texto que hemos
aadido.
Las modificaciones que tenemos que hacer para que la prueba pase con xito
son muy similares a las modificaciones que hicimos para aadir el tipo de evento. Vamos
a comenzar modificando la interfaz (cdigo 7.18).
Cdigo 7.18.
public interface Subscriber<T> {
void newEvent(Event event, T element);
}

Al aadir un parmetro genrico a la interfaz hemos de modificar cualquier
referencia a la misma que tenemos. En la clase Queue hemos de modificar la declaracin
del atributo, el constructor y el mtodo subscribe. Las nuevas implementaciones de
estos tres elementos se muestran en el cdigo 7.19.
Cdigo 7.19.
List<Subscriber<T>> subs;

public void subscribe(Subscriber<T> sub) {
this.subs.add(sub);
}


95

public Queue() {
//.
subs = new ArrayList<Subscriber<T>>();
}

El nico cdigo nuevo que hay que aadir es modificar el mtodo add de la clase
Queue para que incluya el elemento aadido a la llamada a los subscriptores (cdigo
7.20).
Cdigo 7.20.
public void add(T e) {
//.
for (Subscriber<T> sub: this.subs) {
sub.newEvent(Event.Add, e);
}
}

Con el cambio anterior, la prueba ya funciona y ya hemos implementado la
funcionalidad de nuestro diario de diseo.
Con los cambios anteriores todas las pruebas funcionan correctamente. Veamos
qu podemos refactorizar. En las pruebas hemos utilizado una cadena de texto concreta.
Este caso, podemos cambiar dicha cadena por un atributo ya que no nos inters tanto el
valor concreto de la cadena, ni el tipo de dato que almacena la cola, sino el papel que
juega en la prueba.
Cdigo 7.21.
public class TestSubscription {
String anElement = "new string";
//.

@Test
public void
testWhenAnelementIsAddedTwoSubscriptorsReceivesACallInMethod_newEvent_() {

queueWithTwoSubscribers.add(anElement);

verifyNewElementIsCalled(sub01);
verifyNewElementIsCalled(sub02);
}
}


Continuamos con lo que nos queda en el diario de diseo.
96

Hora de borrar
El estado actual de nuestro diario de diseo se muestra en el cuadro 3.5. La ltima
funcionalidad que nos queda por implementar es la notificacin del borrado de un
elemento de la cola.
3.5. Diario de diseo despus de la primera prueba
Permitir a un objeto subscribirse a la cola (clase Queue).
Enviar una notificacin cuando se aada un elemento.
Permitir subscripciones de varios objetos
Enviar la notificacin de aadir a todos los objetos subscritor
Notificar el tipo de evento
Notificar el elemento aadido o borrado
Enviar una notificacin cuando se borre un elemento y no cuando se llame al
mtodo borrar peor nos e borre un ejemplo.


Vamos a dividir la ltima funcionalidad en dos, primero nos preocuparemos de
la notificacin y, despus, comprobaremos que no se enve la notificacin si nos e ha
borrado ningn elemento.
Vamos a escribir una prueba muy similar a la prueba de add (tal y como qued
en el cdigo 7.21), como ya refactorizamos las pruebas apenas hay que repetir cdigo
(cdigo 7.22).
Cdigo 7.22.
@Test
public void
testWhenAnElementIsRemovedSubscriptorsReceivesThe_Remove_EventAndThatEleme
nt() {
queueWithTwoSubscribers.add(anElement);
queueWithTwoSubscribers.drop();

verify(sub01).newEvent(eq(Queue.Event.Drop), eq(anElement));
}

Observa que, en esta prueba, estamos llamando al mtodo add, que genera una
notificacin y al mtodo drop, que queremos que genere otra notificacin. No dara
esta prueba un falso error al confundir la notificacin del mtodo add con la notificacin
esperada de drop?. Por suerte Mockito es capaz de localizar lo que buscamos.
Mockito almacena todas las llamadas que recibe un mtodo. En este caso la
prueba verifica que alguna de las llamadas haya sido pasando como parmetro el
evento event.Drop, cosa que no sucede porque an no hemos escrito el cdigo, por lo
que la prueba falla. Cuando ejecutamos la prueba, Mockito busca la llamada que
coincida con los parmetros indicados entre todas las llamadas a ese mtodo.
Vamos a hacer que la prueba funcione. En este caso, el mtodo drop es
prcticamente igual que el mtodo add por lo que usaremos una implementacin obvia.
97

Adems, hemos aadido el evento event.Drop al enumerado con los tipos de eventos. La
implementacin la tienes en el cdigo 7.23.
Cdigo 7.23.
public static enum Event {Add, Drop};

public T drop() {
T e = // Elemento borrado

for (Subscriber<T> sub: this.subs) {
sub.newEvent(Event.Drop, e);
}

// .
}

Con el cdigo anterior la prueba funciona, pero hemos introducido, al menos, un
mal olor. El mtodo drop es muy similar a add, lo cual es un aviso de que debemos
refactorizar para evitar el cdigo duplicado. El resultado de esta refactorizacin se
muestra en el cdigo 7.24.
Cdigo 7.24.
public void add(T e) {
//.
sendEvent(Event.Add, e);
}

public T drop() {
T e = //

sendEvent(Event.Drop, e);
//.
}

private void sendEvent(Queue.Event event, T value) {
for (Subscriber<T> sub: this.subs) {
sub.newEvent(event, value);
}
}

En el cdigo 7.24 hemos creado un nuevo mtodo privado que contiene el
cdigo compartido por add y remove. Ya estamos a punto de terminar, pero an queda
un ltimo detalle que trataremos en la siguiente seccin.
Solo cuando de verdad se borre
La funcionalidad a implementar es lanzar el evento de borrado de elementos solo
cuando se ha borrado el elemento, pero si la cola est vaca y se intenta borrar, qu
98

sucede? Este detalle depende de la implementacin de la cola, pero podemos
documentarlo en una prueba. Vamos a escribir una prueba que ejecute este escenario y
comprobemos qu sucede (cdigo 7.25).
Cdigo 7.25.
@Test
public void
whenThereAraNotElementsToRemove_SubscriptorsDontReceiveNotification() {
queueWithTwoSubscribers.drop();

verify(sub01, never())
.newEvent(eq(Queue.Event.Drop), any(String.class));
}

En el cdigo 7.25 utilizamos Mockito para verificar que un mtodo no ha sido
llamado nunca Al ejecutar la prueba comprobamos que la clase Queue lanza una
excepcin si se intenta borrar un elemento y la cola est vaca, por lo que ya podemos
dar por concluido este ejercicio.
Borramos la prueba del cdigo 7.25, ya que ni aporta ni documenta nada, dado
que el comportamiento de la cola es distinta del especificado en la prueba y la cola nos e
comporta como la prueba indica. Tampoco tiene sentido modificar la prueba para
verificar que se lanza la excepcin porque esa prueba debe estar en el conjunto de
pruebas de la cola, no en el conjunto de pruebas relacionadas con los suscritores y las
notificaciones.
Tachamos la ltima lnea de nuestro diario de diseo y ya hemos terminado.
Conclusiones
En este captulo hemos visto un ejemplo de uso de un doble de prueba como mock (el
objeto que Mockito crea implementando la interfaz Subscriptor). Este mock ha sido
utilizado como espa con el que hemos verificar que el comportamiento de la cola era el
esperado y se llamaba al mtodo correspondiente con los parmetros vlidos.
El doble de prueba nos ha servido tambin para posponer decisiones de diseo e
ir incorporando detalles poco a poco. Como hemos visto en el captulo, hemos
modificado la interfaz pero no hemos necesitado modificar su implementacin ya que
Mockito la crea para nosotros al ejecutar las pruebas.
En otros captulos este libro encontrars otros de los ejemplos de uso de los
dobles de prueba que se describen en el anexo y seguiremos trabajando con Mockito.




99



Anexo 1.
Test de pruebas


Este test te ayuda a aprender conceptos bsicos de la prueba del software y es una
continuacin del reto de testing publicado en BetaBeers. Si despus de hacer este test
quieres aprender ms, pasa por este enlace y resuelve el reto de testing:
http://betabeers.com/test/testing-16/
Preguntas
1. Por cules de los siguientes motivos elegiras utilizar algn tipo de double?
a) Porque estamos haciendo pruebas unitarias
b) Porque tenemos una dependencia de una clase que an no existe.
c) Porque tenemos una dependencia de una clase u es muy lenta.
d) Todas las anteriores.

2. Qu pruebas debern ejecutarse en primer lugar?
a) Las pruebas que busquen los errores ms frecuentes
b) Las pruebas que busquen errores con un mayor impacto econmico
c) Las pruebas indicadas por los usuarios finales
d) Las prueba sindicadas en el plan de pruebas


100

3. Cuntas particiones son necesarias para probar un mtodo mediante la tcnica de
particiones equivalentes?
a) Tantas particiones como parmetros de entrada tenga un mtodo.
b) Tantas particiones como parmetros de entrada y atributos de la clase que le
afecten
c) Tantas particiones como posibles valores de entrada
d) Tantas particiones como posibles salidas tenga el mtodo.

4. Actualmente, una de las aplicaciones ms habituales de la computacin en la nube
aplicada a la prueba del software es:
a) Ejecutar las pruebas unitarias en la nube para evitar pausas al escribir cdigo.
b) Contratar recursos para simular mltiples usuarios sobre el sistema bajo prueba.
c) Realizar pruebas A/B
d) Almacenar toda la informacin sobre la ejecucin de pruebas

5. Cundo debemos probar interacciones en lugar de estados en una prueba unitaria?
a) Siempre que podamos
b) Nunca
c) Cuando el cdigo bajo prueba no genere ningn resultado verificable
d) Cuando el mtodo a probar no devuelva nada

6. El contenido ms habitual de un plan de pruebas es.
a) Elementos bajo prueba, criterios de aceptacin, entornos de pruebas,
responsables y planificacin
b) Elementos bajo prueba, criterios de aceptacin, entornos de pruebas,
responsables, planificacin, resultados de las pruebas
c) Iteracin, historias de usuario a probar, definicin de hecho, entornos de
pruebas, responsables, planificacin
d) Elementos bajo prueba, criterios de aceptacin y entornos de pruebas


101

7. Un conjunto de pruebas unitarias consume mucho tiempo en su ejecucin. Cul de
las siguientes acciones elegira para aumentar la velocidad?
a) Ejecutar dichas pruebas en un servidor dedicado, por ejemplo un servidor de
integracin continua
b) Intentar sustituir algn mdulo por un doubl / mock /stub
c) Comprar un ordenador ms potente
d) Quitar las pruebas que menos importancia tengan

8. Un compaero de trabajo te hace llegar un fragmento de cdigo fuente indicndote
que falla. Qu haras?
a) Preguntar cul es el resultado esperado despus de la ejecucin de dicho cdigo
b) Ejecutar el cdigo paso a paso con un depurador
c) Escribir varias pruebas unitarias para localizar el error
d) Revisar cuidadosamente el cdigo en busca del error

9. Es recomendable introducir estructuras de control como if o for en las pruebas
unitarias?
a) S porque as se pueden escribir pruebas ms potentes y verstiles que ahorren
tiempo
b) No porque dichas estructuras hacen la prueba ms difcil de entender.
c) S porque con ellas se pueden verificar mejor los resultados de una prueba, con
lo que la eficiencia de la prueba aumenta.
d) No, porque una prueba debe ser simple ya que no escribimos pruebas para
probar pruebas






102

10. Cules seran los valores lmite para probar el siguiente fragmento de cdigo?
method(int a, int b) {
int c = 0;

if ( a > 5) {
c++;
}
if ( b > 6> {
c++:
}
}

a) Para a sera -1, 0 y 1 y los mismos para b
b) Para a sera MAX_INTEGER, - MAX_INTEGER y 0 y los mismos para b
c) Para a sera 5 y 6 y para b 6 y 7
d) Para a sera 4, 5 y 6 y para b 5, 6 y 7




103

Respuestas
Pregunta Respuesta Pregunta Respuesta
1 D 6 A
2 D 7 B
3 D 8 A
4 B 9 D
5 C 10 c

Agradecimientos del captulo
A Miquel Camps por su estupenda iniciativa BetaBeers y por incluir el reto de
testing que le propusimos y que podis acceder en este enlace:
http://betabeers.com/test/testing-16/.
A Antonio Martnez por sus estupendos y valiosos comentarios sobre el reto de
testing, los cules nos han animado a inclus este anexo.



104






Anexo 2.
Dobles de Prueba y Mockito


Este anexo explica el papel que juegan los dobles de prueba a la hora de escribir pruebas
y los tipos de dobles de prueba ms habituales. El anexo incluye tambin ejemplos de
Mockito, una librera multilenguaje que permite crear dobles de prueba de manera
dinmica. Con estos ejemplos no tendrs ningn problema en comprender los captulos
de este libro que utilizan dobles de prueba y Mockito.
Dobles de prueba
Vamos a justificar la utilidad de los dobles de prueba un ejemplo. Supongamos que
queremos probar el cdigo A2.1.
Cdigo A2.1. Mtodo bajo prueba


Cul es el resultado esperado del mtodo descuentoPorPedido? Observa que
este mtodo no devuelve ningn valor, sino que lo que se espera que haga es llamar al
105

mtodo de contar de pedido con el descuento adecuado. Adems, el mtodo necesita
un objeto pedido, un objeto user un objeto descuento y un objeto database o no
podremos ejecutar ninguna prueba del mtodo descuentoPorPedido. No es prctico que
no podamos probar este mtodo hasta que no hayamos escrito todos los objetos
necesarios y que, para escribir una prueba, tengamos que establecer un nmero de
pedidos concreto en la base de datos accedida por el objeto database.
Los dobles de prueba nos van ayudar en este caso, tanto para verificar que se ha
llamado al parmetro correcto como para reemplazar otros objetos que el cdigo bajo
prueba necesite.
Un doble de prueba (test double en ingls) es una implementacin falsa de una
interfaz. Esta implementacin falsa se utiliza durante la ejecucin de pruebas para
reemplazar la implementacin verdadera. Hay varios escenarios en los que podemos
sacarle partido a los dobles de prueba, algunos ya los hemos visto en el ejemplo
anterior. Veamos algunos:
Con los dobles de prueba podemos reemplazar cdigo que accede a sistemas o
servidores externos, por ejemplo bases de datos, API Rest, redes sociales, etc. En este
caso, las pruebas se ejecuten de manera mucho ms rpida al simular un acceso, por
ejemplo, a un servidor de bases de datos en vez de tener que realizar el acceso real.
Otro uso es poder controlar el contexto de la prueba. Podemos configurar el
doble de prueba para que devuelva determinados datos o para que lance un error como
respuesta y as probar el comportamiento de nuestro cdigo.
Un tercer uso de los dobles de prueba es cuando necesitamos probar
interacciones, como en el ejemplo que hemos visto al principio. En determinados casos,
el comportamiento de un cdigo puede ser no generar un valor o cambiar los atributos
de una clase, sino realizar una o varias llamadas a los mtodos de otra clase. Gracias a
los dobles de prueba podemos verificar que dichas llamadas se han ejecutado en el
orden correcto y con los parmetros adecuados.
Un cuarto uso de los dobles de prueba es representar cdigo que an no ha sido
escrito. Por ejemplo en una clase que contengan toda la lgica de conexin a un servidor
externo, acceso a su informacin y desconexin. En este caso, el uso de dobles de
prueba puede ayudarnos a definir la interfaz con precisin y a determinar si estamos
desarrollando una interfaz cmoda y sencilla de usar antes de entrar en los detalles de
implementacin.
Todos estos usos de los dobles de prueba han dado lugar a distintos tipos.
Vamos a ver algunos de estos tipos con ms detalle a continuacin.
106

Tipos de dobles de prueba
Desde hace aproximadamente 5 o 6 aos, se ha empezado a estandarizar una
clasificacin de tipos de dobles de prueba con el objetivo de que todos los que
probamos y escribimos cdigo de prueba tengamos unos conceptos similares y podamos
entendernos ms fcilmente
Podemos clasificar un doble de prueba en funcin de su complejidad y de la
misin que puede desempear. Lo tipos ms comunes de dobles de prueba son:
Dummy: Objeto que necesita el cdigo bajo prueba, pero que nunca ser
utilizado durante la prueba.
Stub: Objetos que devuelven valores predefinidos al llamar a sus mtodos.
Fake: Objeto que implementa un atajo y no es vlido para produccin.
Mock: Objeto programado con las llamadas a mtodos esperadas.
Shim: Este tipo de test doubl est definido en la herramienta Microsoft
Fakes y permite sustituir a referencias incrustadas en cdigo ya escrito sin
necesidad de modificar dicho cdigo. Es posible desarrollar lo mismo en Java
con la herramienta PowerMocks y otras similares.
Veamos un ejemplo para aclarar estos conceptos. Queremos simular el cdigo que
accede a una base de datos, bien para aumentar la velocidad de muestras pruebas, o
para independizarnos del servidor de bases de datos, bien porque el cdigo an no est
escrito, o bien por todas estas razones a la vez.
Si utilizramos un stub para simular este cdigo, tendramos un objeto que siempre
devolver valores prefijados.
Si utilizramos un fake tendramos un objeto que simulara el comportamiento de
una base de datos, por ejemplo utilizando un map para guardar los valores, usando
como clave del map un campo definido como clave primaria. Incluso podra pasar que
nos diramos cuenta de que el map es justo lo que necesitamos y aadiramos un
mecanismo para persistirlo. En este caso el fake pasara a ser un componente de prueba
a cdigo de solucin. Algo similar sucedi durante el desarrollo de FitNesse, como se
comenta al final de este anexo.
Si utilizramos un mock, en cambio, tendramos un objeto al que programaramos
con la secuencia de los mtodos que esperamos (por ejemplo, conectar, realizar una
consulta, desconectar) para verificar si el cdigo est utilizando correctamente el acceso
a la base de datos.
Si utilizramos un dummy, estaramos indicando que el cdigo que vamos a probar
no necesita realizar ninguna operacin con la base de datos.
Por ltimo, si utilizramos un Shim, estaramos instrumentando un cdigo ya escrito
y que no podemos modificar para interceptar las llamadas a mtodos que acceden a la
107

base de datos y redirigirlas a nuestro doble de prueba, por ejemplo, para que se
comporte como un stub.
Veamos otro ejemplo. Vamos retomar el ejemplo con el que abramos este anexo y
vamos a identificar qu tipo de doble de prueba sera ms adecuado para cada una de
las dependencias del mtodo que queremos probar. Por comodidad dicho ejemplo se
muestra de nuevo en el cdigo A2.2.
Cdigo A2.2. Mtodo bajo prueba


Vimos al principio de este apndice as dependencias del mtodo
descuentoPorPedido. Veamos ahora, en la tabla A2.1 cul podra ser el tipo de doble de
prueba que mejor encajara en cada una de ellas.
Tabla A2.3. Ejemplos de dobles de prueba para descuentoPorPedido.
Referencia Tipo de mock
User Dummy porque el mtodo no lo utiliza.
Pedido Mock para poder verificar que se ha realizado la llama al mtodo
descontar
Database Stub porque devolvera el nmero de pedidos necesario para que
el mtodo entre en el if
Descuentos Stub porque se nica misin es devolver un descuento concreto

Felicidades. En este punto no solo sabes qu es un doble de prueba y los tipos
de dobles ms comunes sino que tambin eres capaz de decidir cul es el ms adecuado
en cada situacin y ahrrate tiempo y trabajo desarrollando cosas que no necesitas. En
las prximas secciones vamos a ver cmo poner todo esto en prctica.
Antes de concluir este anexo quiero hacerte una advertencia. Como vimos al
principio de esta seccin, la terminologa utilizada para dobles de prueba se estableci
hace 5 aos aproximadamente. Textos ms antiguos o textos a an no hayan adoptado
estos trminos pueden utilizar nombres distintos o mezclar algunos de los nombres que
hemos visto. Pero no te preocupes, ahora que sabes los nombres y su significado podrs
entender fcilmente cualquier texto sobre dobles de prueba aunque usen los trminos
de manera distinta.
108

Una breve introduccin a Mockito
Siempre que trabajamos con dobles de prueba solemos trabajar con referencias a
interfaces, as podemos cambiar el objeto real por el doble y viceversa fcilmente. Si no,
podemos emplear herencia y redefinir los mtodos, pero debemos evitar utilizar esta
opcin siempre que podamos porque pone de manifiesto un diseo demasiado
acoplado.
No es necesario ninguna librera para trabajas con dobles de prueba. Podemos
escribir nosotros mismos las implementaciones ficticias de estas interfaces para
programar nuestros propios dobles de prueba. Esta solucin es vlida pero requiere
mucho trabajo y muy repetitivo, a pesar de las opciones de los IDEs que nos permiten
escribir implementaciones vacas para los mtodos de una interfaz. Si implementamos
los dobles de prueba a mano hemos de implementar y mantener cdigo para cada
interfaz, hemos de implementar valores devueltos en aquellos mtodos que devuelvan
algo, hemos de implementar comprobantes para verificar que los parmetros son los
esperados en los mtodos que tengan parmetros.
Todo el trabajo anterior es muy repetitivo y, por tanto automatizable. En la
actualidad existen varias herramientas que hacen todo este trabajo por nosotros. En
Java, existen Mockito, JMock, EasyMock o PowerMocks entre otras. En este libro
trabajaremos slo con Mockito aunque los ejercicios que planteamos se podran
resolver con cualquier otra herramienta.
Mockito es una librera Java que permite crear automticamente varios tipos de
dobles de prueba (principalmente mocks y stubs) a partir de interfaces. Permite indicar
el comportamiento de los mtodos de la interfaz cuando sean invocados y tambin
evaluar qu mtodos han sido llamados y con qu parmetros.
En lugar de describir el API de Mockito, vamos a ver ejemplos de cmo trabajar
con Mockito. Varios de estos ejemplos los hemos usado en algunos captulos de este
libro, por lo que te ayudarn a entender las pruebas que se han escrito.
Ejemplos con Mockito
Todos los ejemplos de esta seccin se basan en un doble de prueba de la interfaz
java.util.List creado por Mockito y todos se ejecutan correctamente. Utilizaremos esta
herramienta tanto para crear stubs que devuelvan valores predeterminados, como para
crear mocks que verifiquen llamadas concretas a mtodos. Dejamos al lector que
identifique a qu tipo corresponde cada ejemplo.
En el primer ejemplo (cdigo A2.3), creamos un doble que implementa la
interfaz List y verificamos (con la sentencia verify de Mockito) que se ha llamado al
mtodo add con el parmetro a. Si no llamamos al mtodo add o lo llamamos con un
parmetro distinto a "a" la prueba fallar.
109

Cdigo A2.3.
@Test
public void testMethodAddIsExceutedWithRightParameter() {
List<String> l = mock(List.class);

l.add("a");

verify(l).add("a");
}


En el segundo ejemplo, cdigo A2.4, vemos que el doble de prueba va
almacenando las llamadas y los parmetros recibidos y podemos validarlos en cualquier
orden.
Cdigo A2.4.
@Test
public void testMethodAddIsExceutedSeveralTimes() {
List<String> l = mock(List.class);

l.add("a");
l.add("b");
l.add("c");

verify(l).add("c");
verify(l).add("b");
verify(l).add("a");
}


En el ejemplo del cdigo A2.5, definimos el comportamiento del mtodo get
para que espere el parmetro 0 y devuelva la cadena a. Esto suceder siempre
independientemente del nmero de llamadas, tal y como se muestra en el bucle for.
Cdigo A2.5.
@Test
public void testMethodGetReturns_A_EveryTimeIsCalled() {
List<String> l = mock(List.class);

when(l.get(0)).thenReturn("a");

for (int i = 1; i < 100; i++)
assertEquals("a", l.get(0));
}


En el ejemplo del cdigo A2.6, comprobamos si se ha llamado al mtodo
contains. El primer verify comprueba que la llamada se ha realizado con un parmetro
cualquiera y el segundo comprueba que la llamada se ha realizado con un parmetro de
110

tipo String. Esto nos da ms flexibilidad que el tener que indicar el parmetro concreto
como en los ejemplos anteriores.
Cdigo A2.6.
@Test
public void testRemoveHasBeenCalled() {
List<String> l = mock(List.class);

l.contains("x");

verify(l).contains(any());
verify(l).contains(any(String.class));
}

En el ejemplo del cdigo A2.7, comprobamos que el mtodo get se ha llamado
con un parmetro de tipo entero.
Cdigo A2.7.
@Test
public void testIndexOf_IsCalledWithAnyInteger() {
List<String> l = mock(List.class);

l.get(3);

verify(l).get(anyInt());
}


En el ejemplo del cdigo A2.8 verificamos que el mtodo clear del doble de
prueba no ha sido llamado nunca.
Cdigo A2.8.
@Test
public void testClearMethodWasNotCalled() {
List<String> l = mock(List.class);


verify(l, never()).clear();
}


En el ejemplo A2.9 indicamos que, cuando mtodo get reciba el parmetro 0
devuelva la cadena A, y cuando reciba el parmetro 1 devuelva la cadena B. Al igual
que en el ejemplo del cdigo A2.5, podemos llamar a get(0) y get(1) tantas veces como
queramos.


111

Cdigo A2.9.

@Test
public void testGetIndex_0_Returns_A_AndIndex_1_Returns_B_{
List<String> l = mock(List.class);

when(l.get(0)).thenReturn("A");
when(l.get(1)).thenReturn("B");

assertEquals("A", l.get(0));
assertEquals("B", l.get(1));
}


El ejemplo del cdigo A2.10 configuramos el doble de prueba para que la
primera llamada al mtodo get con el parmetro 0 devuelva la cadena A y las
posteriores llamadas devuelvan la cadena B.
Cdigo A2.10.
@Test
public void testGetIndex_0_Returns_A_AndThenReturns_B_() {
List<String> l = mock(List.class);

when(l.get(0)).thenReturn("A").thenReturn("B");

assertEquals("A", l.get(0));
assertEquals("B", l.get(0));
assertEquals("B", l.get(0));
}


El ejemplo del cdigo A2.11 verifica que el mtodo get ha sido llamado una y
solo una vez. Si el mtodo no es llamado en ninguna ocasin o es llamado ms de una
vez, el verify fallar.
Cdigo A2.11.
@Test
public void testGetIsCalledOnlyOnce() {
List<String> l = mock(List.class);

l.get(0);

verify(l, only()).get(anyInt());
}


Con los ejemplos que acabamos de ver, podrs resolver y entender todos los
ejemplos de este libro. Pero hay tambin muchos ms ejemplos y posibilidades para
utilizar Mockito, no dejes de consultar su documentacin.
112

Errores comunes trabajando con dobles de pruebas
Todo es bueno con moderacin (o es dicen). Los dobles de prueba tambin. Hay algunos
errores habituales cuando se trabaja con dobles de prueba. Vamos a comentarlos
brevemente a continuacin.
El primer error es probar los propios dobles de prueba. Observa el cdigo A2.11.
Eres capaz de descubrir el fallo?
Cdigo A2.11. Ejemplo


Si escribes un caso de prueba en el que verificas un resultado devuelto por un
doble de prueba (en vez de verificar el cdigo bajo prueba o que los mtodos fueron
llamados), est probando el propio doble de prueba, no tu cdigo real. Los ejemplos que
has visto en la seccin anterior te ensean a utilizar Mockito pero nunca debes escribir
pruebas as en tu cdigo o estars probando los dobles de prueba.
Otro error es escribir dobles de prueba demasiado complejos y detallados.
Cuando ms detallado es un doble de prueba, ms rgido es y un pequeo cambio puede
invalidar muchas pruebas y ms difcil es entender su comportamiento.
Un tercer error es utilizar demasiados dobles de prueba. Si necesitas ms de 3 o
4 mocks para una nica prueba es posible que tengas un mal diseo y que merezca la
pena revisar cmo est organizado tu cdigo (como en el ejemplo del cdigo A2.3).
Adems, tus pruebas sern ms difciles de entender y ms frgiles. Utilizando dobles de
prueba verificas el comportamiento del sistema cuando el cdigo se comporta
exactamente igual que el doble de prueba. Si el cdigo exhibe un comportamiento
inesperado o cambia el comportamiento pero no se actualizan los dobles de prueba las
pruebas no te darn la confianza adecuada en que tu sistema funciona correctamente.
Ahora que sabes usar dobles de prueba salos para el bien, no para dispararte
en un pie.
Alternativas a los dobles de prueba
Como hemos visto al principio de este anexo un uso muy habitual de los dobles de
prueba es reemplazar cdigo que accede a servidores y servicios externos. As, por
113

ejemplo, en vez lanzar una consulta a una base de datos, reemplazamos esa clase por un
stub que devuelve un conjunto de datos predefinidos.
Aunque ganamos en velocidad y control de la prueba, nos impide probar el
cdigo que accede a la base de datos. Adems, una interaccin compleja con un sistema
puede llevarnos a necesitar un doble de prueba muy complejo y frgil.
Una alternativa para evitar utilizar dobles de prueba son los servidores en
memoria. Por ejemplo, en el caso de un servidor de BBDD podramos tener nuestro
propio servidor en memoria que se ejecute y se inicialice con los datos necesarios antes
de cada prueba. Si la ejecucin e inicializacin son lo suficientemente rpidas y no
requiere cdigo complejo, es una herramienta vlida escribir pruebas que trabajaran
con l. Algunas referencias a herramientas que podemos utilizar se citan en los
siguientes prrafos. Al final de este anexo se incluye una tabla con los enlaces a todas las
herramientas.
Para bases de datos podemos utilizar bases de datos en memoria, como
Hipersonic SQL (ms conocida por HSQL) o Apache Derby. En este caso, ser necesario
lanzar el servidor, crear la base de datos, y llenarla con los datos necesarios para la
prueba. Para facilitar esta tarea podemos utilizar herramientas adicionales como
DbUnit. Tambin existe un proyecto que adopta la manera de trabajar de esta
herramienta para utilizar repositorios de datos no SQL (como Casandra o MongoDB)
llamada NoSQLUnit.
Para trabajar con correos electrnicos podemos utilizar un servidor de e-mail
embebido, es decir, que forma parte de las libreras de prueba y que podemos encender
y apagar como parte de las pruebas. Una herramienta que nos permite utilizar un
servidor de correo electrnico de una manera similar a lo que hemos visto en el caso
anterior para base de datos es Dumbster y Wiser (incluido en SubEtha SMTP). Ademas,
estas libreras nos permiten crear rpidamente servidores que acepta correos y los
almacenan de manera que pueden ser consultados desde el cdigo de las pruebas.
Para trabajar con APIs Rest tenemos varias opciones, entre ellas WireMock, la
cual nos permite crear stubs y mocks de APIs Rest, indicando las llamadas esperadas a la
API y los datos a devolver en cada llamada.
Tambin existen proyectos que se ejecutan en entornos especiales que
proporcionan una serie de funcionalidades concretas. Si ejecutamos las pruebas fuera
de esos entornos, no tenemos disponible es funcionalidad y el cdigo puede fallar. Por
ejemplo, en el caso de Android, existen herramientas como Roboelectric que permite
emular todas las APIs que Android pone a nuestra disposicin sin tener que ejecutar las
pruebas ni en un emulador ni en un dispositivo android, con la ganancia de velocidad
que ello supone. En el caso de los contenedores de aplicaciones, como J2EE y Glashfish,
contamos con la herramienta Arquillian que nos permite emular el propio contenedor.
114

Para terminar
Hemos visto en este anexo todo lo necesario para comprender qu es un doble de
prueba y qu papel juega, y para empezar a utilizar la librera Mockito en nuestras
pruebas. Si an no lo has hecho, puedes buscar los captulos de este libro en los que se
resuelven katas que involucran dobles de prueba para ver todo lo anterior en accin.
Bola Extra. FitNesse
FitNesse es un buen ejemplo de cmo utilizar dobles de prueba nos lleva a descubrir que
nuestros proyectos pueden funcionar muy bien con soluciones ms sencillas. FitNesse es
un servidor web que proporciona un wiki que permite escribir especificaciones que,
mediante el cdigo adecuado, se pueden ejecutar de manera automtica sobre el
sistema en desarrollo.
En su artculo Professionalism and Test-Driven Development
1
, Robert C. Martin,
uno de los principales desarrolladores de FitNesse cuenta que, al principio del
desarrollo, detectaron que necesitaban una base de datos, pero no tenan informacin
suficiente para decantarse por una base de datos concreta y no queran tomar esa
decisin tan pronto. Para evitarlo crearon un conjunto de mocks con las funciones de
acceso a la base de datos.
Ms adelante, a medida que el desarrollo de FitNesse continuaba, tuvieron la
necesidad de probar funcionalidad que no podan implementar con los mocks. Para ello
crearon un fake consistente en una base de datos en memoria a la que accedan a travs
de la misma interfaz que utilizaban para interactuar con los mocks por lo que no hubo
necesidad de cambiar nada de lo que ya tenan hecho.
Ms adelante necesitaron una autntico mecanismo de persistencia, y para ello
crearon un archivo (flat-file database en el original) que utilizaron como almacn de
datos. Descubrieron que este fichero era suficiente para sus necesidades y no llegaron a
utilizar una base de datos real.
Por ltimo, Robert Martin comenta que conoci a personas que necesitaron una
autntica base de datos SQL y que pudieron integrarla con FitNesse en un solo da de
trabajo.
Referencias
Dos referencias muy tiles para aprender ms sobre dobles de prueba son el libro de
Carlos Bl titulado diseo gil con TDD (http://www.dirigidoportests.com/el-libro) y la
traduccin al espaol del libro Testing Unitaro con Microsoft Fakes
(https://vsartesttoolingguide.codeplex.com/downloads/get/720407). En el primero vas

1
Enlace: http://ieeexplore.ieee.org/xpls/abs_all.jsp?arnumber=4163026
115

a encontrar un captulo entero dedicado a los dobles de prueba y un ejemplo,
desarrollado en varios captulos, en el que se utilizan para desarrollar u problema no
trivial. En el segundo, como su propio nombre indica,
Adems, la tabla A2.2 recoge enlaces a las herramientas mencionadas en este
anexo.
Tabla A2.4. En laces a las herramientas mencionadas en este apndice.
Herramienta Enlace
Apache Derby http://db.apache.org/derby/
Arquillian http://arquillian.org/
DBUnit http://dbunit.sourceforge.net/
Dumbster http://quintanasoft.com/dumbster/
EasyMock http://easymock.org/
FitNesse http://fitnesse.org/
Hipersonic SQL (HSQL) http://hsqldb.org/
JMock http://jmock.org/
Mockito http://code.google.com/p/mockito/
NoSQLUnit https://github.com/lordofthejars/nosql-unit
PowerMock http://code.google.com/p/powermock/
Roboelectric http://robolectric.org/
SubETha SMTP (y Wiser) http://code.google.com/p/subethasmtp
WireMock http://wiremock.org/
Agradecimientos del anexo
A Carlos Bl y Juan Mara Lao por sus estupendos libros y traducciones que citamos un
poco ms arriba como fuentes de conocimiento para aprender ms de dobles de
prueba.
A lex Soto por la estupenda charla sobre Mocking en la Codemotion 2.013 y
que me ha ayudado muchsimo para recopilar herramientas con las que evitar utilizar
mocks (enlace: http://es.slideshare.net/asotobu/resistance-is-futile-mocks-will-be-
assimilated).







116




117





Desarrollo Dirigido por
Pruebas Prctico
Gracias.

Vous aimerez peut-être aussi