Nueva kata en AprendiendoTDD

61 views
Skip to first unread message

Francisco Calles

unread,
Apr 9, 2013, 4:25:27 AM4/9/13
to tdde...@googlegroups.com
Hola a todos,

Hemos publicado la solución a la nueva Kata propuesta por @solveetcom en aprendiendoTDD

https://aprendiendotdd.wordpress.com/ esperamos que os sea útil.

Además le hemos añadido un vídeo de "Big bang theory" en el que Seldom explica como funciona.

Un saludo

Leo Antoli

unread,
Apr 9, 2013, 4:33:12 AM4/9/13
to tdde...@googlegroups.com
teniendo en cuenta que en la serie siempre se saca Spock se podría simplificar mucho el juego ;-)

gracias por compartir la kata.

Saludos,
Leo




2013/4/9 Francisco Calles <franc...@gmail.com>

--
Has recibido este mensaje porque estás suscrito al grupo "TDDev" de Grupos de Google.
Para anular la suscripción a este grupo y dejar de recibir sus correos electrónicos, envía un correo electrónico a tddev-sp+u...@googlegroups.com.
Para publicar una entrada en este grupo, envía un correo electrónico a tdde...@googlegroups.com.
Visita este grupo en http://groups.google.com/group/tddev-sp?hl=es.
Para obtener más opciones, visita https://groups.google.com/groups/opt_out.
 
 

Alfredo Casado

unread,
Apr 9, 2013, 5:04:59 AM4/9/13
to tdde...@googlegroups.com
jeje, leo esta spock infected ya :P

Desde luego este caso encajaría perfectamente con un test "data driven" (http://docs.spockframework.org/en/latest/data_driven_testing.html)

Algunas criticas, constructivas que conste jeje: 

- ¿Para que una interfaz si sólo hay una implementacion? (YAGNI). 
- Ya se que lo del "SUT" lo pone en algunos libros, pero es muy feo. En general me gustan poco los nombres (Juego es muy generico, obtenerResultado no os parece muy generico?, todos los metodos que devuelven un valor podrían llamarse así), Usar TDD también para descrubrir mejores nombres. 
- Haceis una clase de test por cada caso/escenario, y luego cada clase de test con un sólo metodo de test. Por que no haceis una sola clase con un método por caso?. Fijaros la enorme cantidad de código duplicado que teneis en la clase de test.
- Ese switch... hace daño a la vista.



Ricardo Borillo

unread,
Apr 9, 2013, 5:13:54 AM4/9/13
to tdde...@googlegroups.com
Una sugeneracia más desde mi humilde punto de vista :P
A lo del switch añadiría el darle semántica a las deciciones con la
extracción de métodos y eliminar el ternario ...

(jugador2 == Elemento.Lagarto || jugador2 == Elemento.Tijera) ?
GANA_JUGADOR_1 : GANA_JUGADOR_2

Quizá el naming para estas decisiones sería algo como
"piedraGanaTijera" o "piedraGanaLagarto".
Ahora mismo me cuesta tener claras las reglas viendo el código ...

---
Salut,
====================================
Ricardo Borillo Domenech
http://xml-utils.com / http://twitter.com/borillo


2013/4/9 Alfredo Casado <casado....@gmail.com>:

Marcin Gryszko

unread,
Apr 9, 2013, 5:16:43 AM4/9/13
to tdde...@googlegroups.com
Lo que estais poniendo en SetUp es para mi la parte integral de la prueba y deberia y al metodo de prueba. Me apoyo con una cita de "xUnit Test Patterns", pagina 425:

A reasonable compromise is to use Implicit Setup to set up the parts of the fixture that are essential but irrelevant and leave the setup of critical (and different from test to test) parts of the fixture to the individual Test Methods. Examples of “essential but irrelevant” fixture setup include initializing variables with “don’t care” values and initializing hidden “plumbing” such as database connections. Fixture setup logic that directly affects the state of the SUT should be left to the individual Test Methods unless every Test Method requires the same starting state.

Marcin Gryszko      


2013/4/9 Alfredo Casado <casado....@gmail.com>

Leo Antoli

unread,
Apr 9, 2013, 5:33:13 AM4/9/13
to tdde...@googlegroups.com
comparto lo que dice Marcin, aunque veo un poco contradictorio la última parte de la cita: "unless every Test Method requires the same starting state."

Evidentemente en el setup van todas las partes comunes, si no es común no lo pones ahí.

Con lo cual por la cita parece que al final sí le parece bien poner cosas relevantes en el setup.

Saludos,
Leo



Saludos,
Le




2013/4/9 Marcin Gryszko <mar...@grysz.com>

Marcin Gryszko

unread,
Apr 9, 2013, 5:42:35 AM4/9/13
to tdde...@googlegroups.com
Tampoco estoy de acuerdo con la ultima frase; prefiero extraer la logica de setup a un metodo helper y repetirlo en cada metodo. Pero este puede ser un smell que las pruebas se puedan parametrizar y evitar tener N test methods.

Ya que el libro identifica los patrones que siguen las pruebas, es contradictorio en alghunas partes. Alli no encuentras una receta unica para escribir las pruebas sino varios enfoques sobre la organizacion de pruebas.

Leo Antoli

unread,
Apr 9, 2013, 6:19:08 AM4/9/13
to tdde...@googlegroups.com
por cierto chic@s,
a ver si os apuntáis a cosas de más dificultad que sumar números o calcular porcentajes ;-)



Saludos,
Leo



2013/4/9 Marcin Gryszko <mar...@grysz.com>
Tampoco estoy de acuerdo con la ultima frase; prefiero extraer la logica de setup a un metodo helper y repetirlo en cada metodo. Pero este puede ser un smell que las pruebas se puedan parametrizar y evitar tener N test methods.

Ya que el libro identifica los patrones que siguen las pruebas, es contradictorio en alghunas partes. Alli no encuentras una receta unica para escribir las pruebas sino varios enfoques sobre la organizacion de pruebas.

--

Angel Java Lopez

unread,
Apr 10, 2013, 10:46:23 AM4/10/13
to tdde...@googlegroups.com
Francisco!

Gracias por la iniciativa y por compartir.

Me sirvio de base para emprender mi propia version


Ahi van a ver que fui publicando casi por test los commits en GitHub, para dar evidencia del proceso.

Angel "Java" Lopez
@ajlopez



2013/4/9 Francisco Calles <franc...@gmail.com>

--

Xurxo F

unread,
Apr 10, 2013, 2:55:22 PM4/10/13
to tdde...@googlegroups.com
Hola a todos:

Me decido a mostrar mi propia versión. Cualquier comentario es bienvenido.

Del mismo modo que Angel 'Java' hice un Commit por cada verde obtenido, o cada refactorización, y también por cada regla hago dos test (una para A vs. B -> A  y  otra para B vs. A -> A)
Las enumeraciones sólo las creo cuando las necesito. Como durante la kata me doy cuenta de que los if pueden volverse ilegibles, decido meterlo todo en un array de decisiones que va creciendo a partir de entonces conforme lo necesito. Sería necesario una refactorización final para hacerlo más legible, o incluso optar por una solución como la de Ángel.

Soy prinicipiante, así que cuando hago TDD me asaltan siempre bastantes dudas. Me voy a limitar a preguntaros por un par de ellas, aunque seguro que estais cansados de responder a cuestiones como esta:

- De mis lecturas anteriores recuerdo que si se agrega un test que pasa a la primera es que no aporta funcionalidad y por lo tanto es 'inútil'. En esta ocasion hay varios que en el momento no son útiles, pero sé a ciencia cierta que posteriormente si lo son. ¿Los elimino y los replanteo cuando realmente aporten algo?

- En mi caso opté por replantear todo los if en medio de la kata y trasformarlos en un array de decisiones. ¿Cuando es lógico hacer una refactorización de este tipo? ¿Cuando has completado todo el código de una funcionalidad? ¿Nada más ser consciente de que hay una solucion mejor? ¿Cuando la complejidad del método rebasa un límite?


¡Saludos!

Xurxo Fresco


Angel Java Lopez dixo o 10/04/2013 16:46:

Xurxo F

unread,
Apr 10, 2013, 2:56:55 PM4/10/13
to tdde...@googlegroups.com
Oppps!

Se me olvidó poner el enlace!

https://github.com/xurxof/TDDPiedraPapelTijeraLagartoSpock

Saludos

Xurxo F dixo o 10/04/2013 20:55:

Alfredo Casado

unread,
Apr 10, 2013, 5:32:22 PM4/10/13
to tdde...@googlegroups.com
Hola xurxo, sobre tus dudas:

Si haces un test que pasa a la primera se pueden diferenciar dos casos:

  - haces el test sin saber que va ha pasar, cuando ocurre esto en cierta manera has perdido algo de control sobre lo que hace tu código, quizas algún paso demasiado largo o complejo.
  - haces el test y sabes a ciencia cierta que va ha pasar. En este caso no es un test que guie tu diseño, es un test que añades porque quieres tener seguridad de que ese caso funciona y que te sirva posteriormente como test de regresión. Yo estos test los intentaría dejar para el final en la medida de lo posible. 

Sobre cuando refactorizar, cuanto antes lo hagas menos trabajo cuesta, vamos que en cuanto tu "sentido arácnido" se dispare y el tema huela mal... ese es el momento.

Sobre los test:

yo intentaría hacer algo más semantico, que tu código se lea, un ejemplo:

public void TijerasPiedra_Piedra() {
            Values CurrentVal1 = Values.Tijera;
            Values CurrentVal2 = Values.Piedra;
            Result CurrentResult = _SUT.Solve(CurrentVal1, CurrentVal2);
            Assert.AreEqual(CurrentResult, Result.SecondWin);
}

lo cambiamos por esto otro:

public void The_Second_Player_Win_When_The_First_Select_Tijera_And_The_Second_Select_Piedra() {
theSecondPlayerWinWhen(TheFirstPlayerSelectTijera, andTheSecondPlayerSelectPiedra)
}

private void theSecondPlayerWinWhen(firstPlayerSelection, SecondPlayerSelection) {
Assert.AreEqual(_SUT.Solve(firstPlayerSelection, SecondPlayerSelection), Result.SecondWin);
}

TheFirstPlayerSelectTijera y andTheSecondPlayerSelectPiedra son simplemente constantes que defines a nivel de la clase de test.

Fijate que esos nombres "currentVal1" o "currentVal2" no aportan información (cuando un nombre tenga un número... mala cosa), otra cosa que debería alertarte es que existe muchisima duplicación en los test, son todos prácticamente iguales. (Aunque en realidad la solución que a mi me gusta para este caso es hacer un test datadriven, no se si .net existe algún framework que te permita algo así, supongo que algo habrá)




Xurxo F

unread,
Apr 10, 2013, 6:21:56 PM4/10/13
to tdde...@googlegroups.com
Hola Alfredo

Gracias por los consejos. En la próxima kata los tendré en cuenta.

Xurxo

 
Alfredo Casado dixo o 10/04/2013 23:32:

Diego Güemes

unread,
Apr 13, 2013, 10:34:33 AM4/13/13
to tdde...@googlegroups.com
Hola chicos.

Me han encantado vuestras soluciones para ver distintas opiniones a la kata.

Alfredo, el código de tu último mensaje me parece una pasada a través de ese mini DSL, muy elegante, aunque, ¿crees que en este caso sería útil? Lo comento porque la línea dentro del test se lee exactamente igual que el nombre del test. No obstante, como digo, me parece una pasada como has jugado con el lenguaje para que se lea como prosa ;).

Por otra parte, me he fijado también en la duplicación de los tests, ya que para cada posible jugada se prueba para el primer y segundo jugador. ¿Se podría crear un matcher para probar la jugada en los dos sentidos y eliminar duplicación? ¿Qué opináis?

Gracias por compartir, un saludo
Diego.

Alfredo Casado

unread,
Apr 13, 2013, 2:39:41 PM4/13/13
to tdde...@googlegroups.com
Hola diego, pues como dices el contenido del test y el nombre están completamente duplicado y eso no huele bien. Se podría reducir la cosa a tres test, uno para los juegos empatados, otro para cuando gana el primero y otro para cuando el segundo,  algo como:

class JuegoPiedraPapelTijeraLagartoSpockTest

,,,

public void can_detect_when_the_second_player_win() {
theSecondPlayerWinWhen(TheFirstPlayerSelectTijera, andTheSecondPlayerSelectPiedra)
theSecondPlayerWinWhen(TheFirstPlayerSelectTijera, andTheSecondPlayerSelectPiedra)theSecondPlayerWinWhen(TheFirstPlayerSelectTijera, andTheSecondPlayerSelectPiedra) }

Alfredo Casado

unread,
Apr 13, 2013, 2:49:35 PM4/13/13
to tdde...@googlegroups.com
Perdon que se me fue el dedo y envie antes de tiempo jeje:

class JuegoPiedraPapelTijeraLagartoSpockTest
,,,
public void can_detect_when_the_second_player_win() {  
theSecondPlayerWinWhen(TheFirstPlayerSelectTijera, andTheSecondPlayerSelectPiedra)
theSecondPlayerWinWhen(...)
theSecondPlayerWinWhen(...) }

Ahora si intentas leer el nombre dela clase junto con los nombres de test tendrías algo como:

JuegoPiedraPapelTijeraLagartoSpock can_detect_when_the_second_player_win
JuegoPiedraPapelTijeraLagartoSpock can_detect_when_the_first_player_win
JuegoPiedraPapelTijeraLagartoSpock can_detect_a_draw_game

El truco es siempre usar el nombre de la clase de test como sujeto y luego el nombre de test, si de esta forma obtienes una especificación textual de lo que hace tu clase entonces los nombres son buenos. A mi me parece que así queda chulo.

Lo de la regla de un sólo assert es una simplificación en mi opinión excesiva, cada test prueba un caso diferente y tu eliges la granularidad de esos casos, mientras todos estén al mismo nivel y no sea una granularidad excesiva a mi me parece aceptable.


Aunque como digo este test es un claro ejemplo de un data driven, con spock seria algo como:

def "Piedra, papel, tijera, largto, spock game scenarios"() {
expect: game.(firstPlayerSelection, SecondPlayerSelection) == expectedWinner

ehere:
firstPlayerSelection | SecondPlayerSelection | expectedWinner
Tijera | Piedra  | SecondPlayerWin
Tijera | Tijera  | Draw
Tijera | Spock   | SecondPlayerWin
}

De esta manera queda una tabla que representa exactamente las normas del juego de la más forma más clara que se me ocurre

Diego Güemes

unread,
Apr 13, 2013, 3:54:34 PM4/13/13
to tdde...@googlegroups.com
Me gusta mucho la solución data-driven, porque aparte de ser clara es más breve que las anteriores.

Lo de los escenarios de gana el primero, gana el segundo y empatan me estaba rondado la cabeza. Tal vez, la clase del juego pudiera ser encargada de detectar el ganador y no de aplicar las reglas del juego, siendo estas últimas clases distintas.

Tengo que ponerme a trastear con esta kata a ver qué sale.

Muchas gracias,
Diego.

Alfredo Casado

unread,
Apr 13, 2013, 4:06:26 PM4/13/13
to tdde...@googlegroups.com
En java los enumerados pueden tener métodos, en C# no lo se la verdad, se podría meter en el enumerado un método "wins" y el código sería algo como:

Tijera.wins(Piedra)

que simplemente devolvería true/false.

De este modo puedes implementar las reglas del juego en el enumerado y la clase "juego" simplemente utiliza estas clases para detectar el ganador, de esta manera la prueba de la clase juego la puedes hacer usando mocks y sólo necesitas probar 3 casos en esa clase.

Si no puedes hacer lo del número bastaría con una clase "GameElement" (no se me ocurre mejor nombre ahora mismo jeje) que podría ser una inner class del juego (tampoco si si esto es posible en c# pero supongo que si).

Desde luego eso de probar en las dos direcciones como decias antes huele mal, de esta forma lo evitas, tienes unos test que prueban puramente las reglas del juego y otros que prueba el juego y si este es capaz de detectar al ganador. Es una mejor separación de responsabilidades, por ejemplo, si queremos ampliar el juego a 3 jugadores, con esta nueva estructura sería mucho más facil.

Diego Güemes

unread,
Apr 13, 2013, 4:22:34 PM4/13/13
to tdde...@googlegroups.com
Estoy de acuerdo. 
Incluso la clase Juego podría utilizar reglas de diferentes juegos, como podría ser la versión clásica de Piedra-Papel-Tijera, sin modificar la clase Juego ni su clase de tests.
Reply all
Reply to author
Forward
0 new messages