presentación + php simplestub

29 views
Skip to first unread message

mariotux

unread,
Jan 30, 2011, 12:46:51 PM1/30/11
to tdde...@googlegroups.com
Buenas,

Hasta ahora he estado de mirón en la lista ^_^, soy desarrollador en
LAMP y me estoy iniciando en el mundillo del TDD.

Me han animado a presentar en la lista una pequeña clase para hacer
stubs en PHP, que tiene ciertas particularidades con el uso de getMock
de PHPUnit para stubear.

El proyecto de SimpleStub todavía le falta rodaje, casos de uso y ver si
merece la pena implementar más opciones. Por ahora tiene pendiente el
retorno de excepciones, aunque ya se está trabajando en ello todavía no
está publicada ninguna versión que las soporte.

Lo que más me ha gustado de hacer esta pequeña clase, es que he
aprendido como tiene que comportarse un Stub y en que casos deberíamos
usarlo.

A diferencia del Stub de PHPUnit, si quieres stubear un método de la
clase que pasas como colaborador, no es necesario stubear el resto de
métodos públicos si ya los tienes implementados.

La idea surgió tras el Code Retreat de Donosti en Noviembre tras una
iteración con Carlos Ble.

Podéis ver el código en: https://github.com/mariotux/php-simple-stub

Este es mi primer mensaje, pero estoy a la espera del mensaje de
Guillermo (@ggalmazor) comenzar un debate que me parece interesante para
ir conociendo más el mundo del TDD.

Salu2.

Mario Nunes

Carlos Ble

unread,
Jan 31, 2011, 6:33:35 PM1/31/11
to tdde...@googlegroups.com
Hola Mario!
Como ya hemos hablado, pinta muy bien. Pero en tu correo no se ve muy clara cual es la diferencia con PHPUnit y en github hecho de menos un txt explicando cómo se usa. Asi a bote pronto.

Dale caña a ver en qué queda la cosa :-)

Buen trabajo!

2011/1/30 mariotux <ma...@pensandoenred.com>

--
Has recibido este mensaje porque estás suscrito al grupo "TDDev" de Grupos de Google.
Para publicar una entrada en este grupo, envía un correo electrónico a tdde...@googlegroups.com.
Para anular tu suscripción a este grupo, envía un correo electrónico a tddev-sp+u...@googlegroups.com
Para tener acceso a más opciones, visita el grupo en http://groups.google.com/group/tddev-sp?hl=es.




--
Carlos Ble
www.MavenCharts.com
www.iExpertos.com
www.carlosble.com

Guillermo Gutiérrez

unread,
Feb 1, 2011, 10:27:00 AM2/1/11
to tdde...@googlegroups.com
Hola a todos!

Este es mi primer mensaje en la lista, así que aprovecho para mandaros un saludo a todos. Espero que mi interacción en esta lista sea enriquecedora para todos :)

El asunto al que hace referencia @mariotux es relativo a un caso de uso de stubs muy específico que intentaré explicaros ahora para que podamos comentarlo.

En primer lugar, me gustaría aclarar que soy un total novato en TDD, por lo que es probable que el problema que os voy a describir a continuación no justifique la funcionalidad extra que voy a proponer para los stubs, sino que se puede solventar corrigiendo la estrategia de testeo o por algún otro mecanismo. No os cortéis en corregir cualquier metedura de pata que probablemente esté metiendo.

Para no andarme por las ramas (luego me explayo con un caso de uso concreto), la funcionalidad "extra" que creo que sería interesante en un stub es la capacidad de devolver un valor determinado en función de los parámetros de llamada a un método. Algo así como:

stub.method("pow2").returnsValue(4).whenCalledWith(2);
stub.method("pow2").returnsValue(9).whenCalledWith(3);


Viene a ser algo a medio camino entre un stub y un mock, aunque en principio no interesa saber si se ha llamado al método pow2() ni saber con qué parametros se ha hecho. En lenguajes como Python, en los que los dobles se pueden definir en el mismo test, no supone ningún problema programar este comportamiento, pero (al menos en PHP) no conozco ningún framework de stubbing/mocking que permita algo parecido.

El caso de uso concreto se podría dar en una clase que implementa un cajero automático en el cual se pueden hacer reintegros, depósitos y consultar el histórico de operaciones. Los reintegros se penalizan con una comisión, por lo que si uno saca 10€, se le deducen de la cuenta 10€ + comisiones. Las comisiones se obtienen por medio de un colaborador que hace uso de un recurso externo (por lo que hay que usar un doble).

Una manera de testear el histórico de operaciones sería realizar un par de depósitos y un par de reintegros y comprobar que la lista de operaciones es la esperada y aquí es donde creo que bien bien toda esta historia. El código del test sería más o menos:

testHistorico() {
    comisiones = new Stub(Comisiones)
    comisiones.method("calcular").returnsValue(2).whenCalledWith(10)
    comisiones.method("calcular").returnsValue(3).whenCalledWith(100)
    cajero = new Cajero(comisiones)
    cajero.deposito(20)
    cajero.reintegro(10)
    cajero.deposito(400)
    cajero.reintegro(100)
    actual = cajero.getHistorico()
    expected = [20,-12,400,-103]
    assertEquals(expected, actual)
}


La idea es que Cajero usa el método calcular() de su colaborador Comisiones, que hemos stubeado. Este tipo de test exige poder configurar el comportamiento del stub de manera que le devuelva a Cajero un 2 para el reintegro de 10 y un 3 para el reintegro de 100.

Pienso que eso es todo. Espero vuestros comentarios :)

Guille.

Alberto Peña

unread,
Feb 1, 2011, 6:16:27 PM2/1/11
to tdde...@googlegroups.com
Hola y bienvenidos (Guillermo y mariotxu)

La verdad es que nunca me había planteado que un stub me devolviera diferentes valores en función del argumento con el que llamo al método. Creo que nunca lo he necesitado :?


Respecto al caso que propones, creo que estás haciendo un test demasiado "grande" (o, más bien, algo redundante). Yo intentaría testear el histórico de otra forma. Primero comprobaría que, si hago un depósito y consulto el histórico, éste contiene dicho depósito (esto se correspondería con "Guardar los depósitos en el histórico). Después haría lo mismo con el reintegro (solo con uno) y usaría un stub para las comisiones pero sin importarme realmente el valor de entrada (Guardar los reintegros en el histórico). Después, y solo si no me fío de mi mismo, quizás haría un test con un depósito y un reintegro, aunque pudiera ser que este último test fuera redundante (No se que comportamiento define este test, así que es probable que me sobre).


Un saludo

2011/2/1 Guillermo Gutiérrez <ggal...@gmail.com>

Alfredo Casado

unread,
Feb 1, 2011, 7:27:49 PM2/1/11
to tdde...@googlegroups.com
Yo estoy con alberto. Por ejemplo fijate en el nombre del test "testHistorico" me dice que elemento de tu sistema estas testeando, pero no me dice que característica de tu sistema estas testeando. En realidad CajeroTest sería un buen nombre para la clase de test y luego tendrías metodos para probar cada una de las funcionalidades de tu test. Obtener el historico de en si no es una funcionalidad, una funcionalidad es que el historico puede guardar reintegros, otra es que puede guardar depositos, y por ejemplo otra que me invento :P es que guarde los 10 últimos movimientos.

Esta es una forma habitual de nombrado para los test, utilizando el nombre de la clase de test como parte inicial de la oración y luego el nombre del método te sale una especificación del objeto que estas testeando, por ejemplo para el caso anterior tendrías:

- El cajero me permite consultar el ultimo deposito realizado a través de su historico
- El cajero me permite consultar el ultimo reintegro realizado a través del cajero
- El cajero me permite consultar los últimos 10 movimientos a través de su historico.

Incluso viendo esto me da que hay un olor de diseño de los test, estas intentando probar el historico a través del cajero. Quizá deberías plantearte que estas funcionalidades encajarían mejor en un test para la clase Historico, y para la clase Cajero lo único que tendrías que testear es que cuando se hace un deposito o reintegro lo hace en el historico (esto si lo podrías probar a través de un mock).

Asi que yo al final tendría dos clases de test:

CajeroTest con dos métodos:

    guarda_los_depositos_en_el_historico
    guarda_los_reintegros_en_el_historico


HistoricoTest con tres métodos

    me_permite_consultar_el_ultimo_deposito_guardado
    me_permite_consultar_el_ultimo_reintegro_guardado
    me_permite_consultar_los_ultimos_10_movimientos

Ahora tienes una especificación tanto del cajero como del historico mucho más rica. Y mira, para ninguno de estos test nos hace falta el stub que devuelva algo distinto en función de un parametro. Además te van a salir test que cubren la misma funcionalidad pero mucho más enfocados y concretos, muy faciles de leer y que describen con mas precisión tu sistema.

Dicho esto, la funcionalidad de devolver algo en función de un parametro de entrada es muy habitual en los frameworks de dobles de prueba y yo la he usado muchas veces, y alberto seguro que también aunque ahora no se acuerde :P, por ejemplo en la ultima entrada que escribí sobre dobles de prueba lo uso.    

Alberto Peña

unread,
Feb 1, 2011, 7:45:59 PM2/1/11
to tdde...@googlegroups.com
El 2011/2/2 Alfredo Casado <casado....@gmail.com> dijo

Dicho esto, la funcionalidad de devolver algo en función de un parametro de entrada es muy habitual en los frameworks de dobles de prueba y yo la he usado muchas veces, y alberto seguro que también aunque ahora no se acuerde :P, por ejemplo en la ultima entrada que escribí sobre dobles de prueba lo uso.    


jejeje según escribía eso pensaba "Ahora vendrá Alfredo y me recordará que sí que los hemos usado" :P En fin, memoria de pez que tengo.

Aún así, al menos ultimamente :D siempre que utilizo un stub y me veo haciendo algo así me pregunto ¿por que? En el caso de el post de Alfredo, este test:
    @Test public void
    si_el_cliente_tiene_fondos_para_realizar_la_compra_la_puedo_confirmar() {
       when(unProductoStub.precio()).thenReturn(50);
       when(otroProductoStub.precio()).thenReturn(25);
       when(pasarelaDePagoStub.tieneElUsuarioFondosPorValorDe(75)).thenReturn(true);
        
       compra.anadir(unProductoStub);
       compra.anadir(otroProductoStub);
   
       assertTrue(compra.confirmar());
    }
y este otro (fijaos que me da igual lo que le llegue a la pasarela. Hay un any() como argumento)

    @Test public void
    si_el_cliente_tiene_fondos_para_realizar_la_compra_la_puedo_confirmar() {
       when(pasarelaDePagoStub.tieneElUsuarioFondosPorValorDe(any())).thenReturn(true);
        
       compra.anadir(unProductoStub);
       compra.anadir(otroProductoStub);
   
       assertTrue(compra.confirmar());
    }

Son equivalentes y el segundo me parece algo más robusto y un poco menos acoplado a la implementación. Además, expresa mejor lo que pretende el test. Queremos comprobar que, cuando un cliente tiene fondos, puede comprar. Me da igual cuantos fondos tenga y cuanto le cuesten los productos.

A lo que realmente quiero llegar es a que no deberían ser importantes los parámetros de entrada que les llegan a los stubs. A mi me parece un olor y creo que hace que el test sea menos robusto. ¿Qué pensáis vosotros?
 
Un saludo

Alfredo Casado

unread,
Feb 1, 2011, 8:05:13 PM2/1/11
to tdde...@googlegroups.com
Pues si, tienes razón y el test tal y como esta prueba más cosas de las que dice, además de probar que el cliente si tiene fondos puede realizar la compra prueba "de paso" que la compra realiza correctamente el calculo del precio.

Es un olor de diseño porque el calculo de la compra o bien esta en algún colaborador que contenga la estrategia del calculo de compra o bien puede ser un método público de la compra, no necesito comprobarlo en ese test y lo único que consigo haciendo eso es atar más el test a la implementación.

Era un ejemplo para demostrar el uso de stubs y mocks en el mismo método y no se me ocurrió hacerlo mejor, pero es un mal diseño y ese test lo demuestra. No se me ocurre ningún caso ahora mismo donde sea imprescindible definir lo que devuelve el stub en función del parametro de entrada, pero de eso a decir que es un mal olor siempre... tampoco estoy seguro porque es un mal olor programar así los stubs, en principio no veo que tenga nada "intrinsecamente" malo.

Alberto Peña

unread,
Feb 1, 2011, 8:42:19 PM2/1/11
to tdde...@googlegroups.com
El 2011/2/2 Alfredo Casado <casado....@gmail.com> dijo
Pues si, tienes razón y el test tal y como esta prueba más cosas de las que dice, además de probar que el cliente si tiene fondos puede realizar la compra prueba "de paso" que la compra realiza correctamente el calculo del precio.

Es un olor de diseño porque el calculo de la compra o bien esta en algún colaborador que contenga la estrategia del calculo de compra o bien puede ser un método público de la compra, no necesito comprobarlo en ese test y lo único que consigo haciendo eso es atar más el test a la implementación.


A eso iba. Normalmente, cuando especificamos los valores de entrada es porque estamos comprobando diferentes comportamientos con un solo test.

 
Era un ejemplo para demostrar el uso de stubs y mocks en el mismo método y no se me ocurrió hacerlo mejor, pero es un mal diseño y ese test lo demuestra. No se me ocurre ningún caso ahora mismo donde sea imprescindible definir lo que devuelve el stub en función del parametro de entrada, pero de eso a decir que es un mal olor siempre... tampoco estoy seguro porque es un mal olor programar así los stubs, en principio no veo que tenga nada "intrinsecamente" malo.

No se, puede ser que no sea un olor, pero es algo que, al menos últimamente :P, me dice que estoy haciendo  algo mal. Más que nada por lo que has dicho antes ( y que ya he repetido :D). Me da la impresión de que intentamos probar varias cosas a la vez. Además, los argumentos de entrada que reciben los colaboradores de una clase es un "detalle de implementación" que acopla el test al código. Esto es así.
Otra cosa es que usemos mocks y hagamos algo como esto:

 @Test public void
    al_comprar_llamamos_a_la_pasarela_de_pago_con_el_valor_total_de_la_compra() {
       when(unProductoStub.precio()).thenReturn(50);
       when(otroProductoStub.precio()).thenReturn(25);

       compra.anadir(unProductoStub);
       compra.anadir(otroProductoStub);
     compra.confirmar();

      expect(pasarelaDePagoStub.tieneElUsuarioFondosPorValorDe(75));

    }

En este caso, estamos probando algo muy diferente y sí que tiene sentido que el "expect" tenga un valor definido. (En este caso, los productos son stubs y solo me interesa lo que devuelven)

A ver si me aclaro. En este caso, el colaborador "pasarela de pago" es un mock y por eso tiene sentido que especifiquemos lo que recibe, ya que es justo lo que queremos probar. En el caso del anterior mensaje, el colaborador "pasarela de pago" es un stub y y me da igual lo que reciba porque solo me interesa definir su comportamiento (que acepta el pago). ¿Ayuda esto en algo?

Cuando a un colaborador del que solo nos interesa el resultado de su colaboración (stub) le definimos los argumentos de entrada creo que, en cierto modo, estamos creando un "expect", ya que nuestro test solo va a funcionar SI Y SOLO SI el stub recibe dicho argumento de entrada. Es decir, el colaborador actua como mock y como stub a la vez, dejándonos con el culo al aire. A ver si aclaro un poco mis ideas y escribo un post sobre esto :P

Disclaimer:
Cuando digo últimamente es hace 3 meses o así (hasta donde me llega la memoria :D), por lo que tampoco hay que tomarme muy en serio. De todas formas, visto lo visto, probablemente dentro de unos meses dire algo así como "por supuesto que hay que pasar los argumentos a los stubs. Siempre lo he hecho así y creo que no hacerlo así es un olor" :P

Un saludo 

Guillermo

unread,
Feb 2, 2011, 2:11:36 AM2/2/11
to tdde...@googlegroups.com
Muchas gracias por vuestras respuestas.

Las conclusiones entonces parecen las siguientes:

1) salvo en casos excepcionales, es preferible reorientar el test hacia otro tipo de prueba
2) tiene sentido que un stub pueda devolver un valor distinto en función de parámetros

Claramente, en mi ejemplo, lo que procede es reorientar el test.

Perdonad la brevedad del mensaje, pero escribo de camino al curro :P

Guille
Enviado desde mi iPhone

mariotux

unread,
Feb 3, 2011, 2:33:43 AM2/3/11
to tdde...@googlegroups.com
Buenas,

Yo sigo sin entender el porqué de que un stub tenga que entender de
parámetros. Lo que yo entiendo de un stub, es que deseamos que cuando se
invoque a un método X nos retorne el valor Y.

Si queremos controlar los parámetros usaríamos un Mock? Todavía no me he
metido mucho con esta parte pero, entiendo que el Mock es para controlar
si la invocación al método se realiza y con que parámetros.

No acabo de ver el poder stubear un método aplicándole una lógica.

Si stubeo un colaborador, entiendo que cuando la clase que estoy
testeando vaya a invocar el método de ese colaborador yo espero que
retorne Y para el test que estoy haciendo.

Si quiero comprobar la lógica del método de ese colaborador, el mismo,
tendrá sus tests.

Para que aplicar una lógica en el stub?

Pero como he comentado en el inicio de este hilo, soy novato y estamos
aprendiendo :-) Iluminarnos Maestros del TDD ^_^

Salu2.

Mario Nunes

mariotux

unread,
Feb 3, 2011, 2:36:35 AM2/3/11
to tdde...@googlegroups.com
A ver si en un par de días lo completo, gracias.

Guillermo Gutiérrez

unread,
Feb 3, 2011, 4:05:34 AM2/3/11
to tdde...@googlegroups.com
Buenas!

Aunque no he tenido tiempo de estudiarlo con detenimiento (tiene
much�sima miga), he le�do las definiciones de Test Stub y Fake Object de
xunitpatterns.com. No conoc�a la web y la cantidad de literatura que hay
al respecto es abrumadora.

Definici�n de un Test Double: http://xunitpatterns.com/Test%20Double.html
Definici�n de un Test Stub: http://xunitpatterns.com/Test%20Stub.html
Definici�n de un Fake Object: http://xunitpatterns.com/Fake%20Object.html

En principio no parece estar expl�citamente definido si un stub debe
devolver el mismo valor o distintos valores. Volviendo a la
justificaci�n de todo este tinglado, parece que lo que importa es poder
usar *un doble* para sustituir un componente que est� fuera de nuestro
contexto de testeo. Luego, en funci�n de aquello que estemos
sustituyendo por un doble, tendremos que optar por uno u otro tipo de
implementaci�n: desde clases hechas a mano a tal efecto, hasta usando
clases definidas din�micamente con frameworks como jmock, mockito, etc.

Pienso que toda la confusi�n que tengo en este sentido se debe a c�mo
est�n implementados los frameworks de mockeo en PHP, ya que ninguno
permite distintos valores de retorno en funci�n de ciertas condiciones.
jMock, por ejemplo, permite esto de f�brica por medio de dos mecanismos:
distintos valores en llamadas consecutivas y condicionadores de retorno
tipo "with(blablabla).returnValue(blablabla)". Por no hablar de la
libertad que tenemos de crear una clase a mano que haga literalmente lo
que nos d� la gana.

Insisto, todo esto sin tener en cuenta si es lo m�s apropiado o no a la
hora de definir nuestros tests unitarios.

On 03/02/11 08:33, mariotux wrote:
> Buenas,
>

> Yo sigo sin entender el porqu� de que un stub tenga que entender de
> par�metros. Lo que yo entiendo de un stub, es que deseamos que cuando se
> invoque a un m�todo X nos retorne el valor Y.
>
> Si queremos controlar los par�metros usar�amos un Mock? Todav�a no me he


> metido mucho con esta parte pero, entiendo que el Mock es para controlar

> si la invocaci�n al m�todo se realiza y con que par�metros.
>
> No acabo de ver el poder stubear un m�todo aplic�ndole una l�gica.


>
> Si stubeo un colaborador, entiendo que cuando la clase que estoy

> testeando vaya a invocar el m�todo de ese colaborador yo espero que


> retorne Y para el test que estoy haciendo.
>

> Si quiero comprobar la l�gica del m�todo de ese colaborador, el mismo,
> tendr� sus tests.
>
> Para que aplicar una l�gica en el stub?


>
> Pero como he comentado en el inicio de este hilo, soy novato y estamos
> aprendiendo :-) Iluminarnos Maestros del TDD ^_^
>
> Salu2.
>
> Mario Nunes
>

> El mi�, 02-02-2011 a las 08:11 +0100, Guillermo escribi�:


>> Muchas gracias por vuestras respuestas.
>>
>>
>> Las conclusiones entonces parecen las siguientes:
>>
>>
>> 1) salvo en casos excepcionales, es preferible reorientar el test
>> hacia otro tipo de prueba
>> 2) tiene sentido que un stub pueda devolver un valor distinto en

>> funci�n de par�metros


>>
>>
>> Claramente, en mi ejemplo, lo que procede es reorientar el test.
>>
>>
>> Perdonad la brevedad del mensaje, pero escribo de camino al curro :P
>>
>>
>> Guille
>> Enviado desde mi iPhone
>>

>> El 02/02/2011, a las 02:42, Alberto Pe�a<plag...@gmail.com>
>> escribi�:


>>
>>
>>
>>> El 2011/2/2 Alfredo Casado<casado....@gmail.com> dijo

>>> Pues si, tienes raz�n y el test tal y como esta prueba m�s
>>> cosas de las que dice, adem�s de probar que el cliente si


>>> tiene fondos puede realizar la compra prueba "de paso" que
>>> la compra realiza correctamente el calculo del precio.
>>>
>>>

>>> Es un olor de dise�o porque el calculo de la compra o bien
>>> esta en alg�n colaborador que contenga la estrategia del
>>> calculo de compra o bien puede ser un m�todo p�blico de la
>>> compra, no necesito comprobarlo en ese test y lo �nico que
>>> consigo haciendo eso es atar m�s el test a la
>>> implementaci�n.


>>>
>>>
>>>
>>>
>>> A eso iba. Normalmente, cuando especificamos los valores de entrada
>>> es porque estamos comprobando diferentes comportamientos con un solo
>>> test.
>>>
>>>
>>>
>>>
>>> Era un ejemplo para demostrar el uso de stubs y mocks en el

>>> mismo m�todo y no se me ocurri� hacerlo mejor, pero es un
>>> mal dise�o y ese test lo demuestra. No se me ocurre ning�n


>>> caso ahora mismo donde sea imprescindible definir lo que

>>> devuelve el stub en funci�n del parametro de entrada, pero


>>> de eso a decir que es un mal olor siempre... tampoco estoy

>>> seguro porque es un mal olor programar as� los stubs, en


>>> principio no veo que tenga nada "intrinsecamente" malo.
>>>
>>>
>>>
>>> No se, puede ser que no sea un olor, pero es algo que, al menos

>>> �ltimamente :P, me dice que estoy haciendo algo mal. M�s que nada


>>> por lo que has dicho antes ( y que ya he repetido :D). Me da la

>>> impresi�n de que intentamos probar varias cosas a la vez. Adem�s,


>>> los argumentos de entrada que reciben los colaboradores de una clase

>>> es un "detalle de implementaci�n" que acopla el test al c�digo. Esto
>>> es as�.


>>> Otra cosa es que usemos mocks y hagamos algo como esto:
>>>
>>>
>>> @Test public void
>>>
>>>
>>> al_comprar_llamamos_a_la_pasarela_de_pago_con_el_valor_total_de_la_compra() {
>>>
>>>
>>> when(unProductoStub.precio()).thenReturn(50);
>>>
>>> when(otroProductoStub.precio()).thenReturn(25);
>>>
>>>
>>>
>>> compra.anadir(unProductoStub);
>>>
>>> compra.anadir(otroProductoStub);
>>>
>>>
>>> compra.confirmar();
>>>
>>>
>>>
>>> expect(pasarelaDePagoStub.tieneElUsuarioFondosPorValorDe(75));
>>>
>>>
>>>
>>> }
>>>
>>>
>>>

>>> En este caso, estamos probando algo muy diferente y s� que tiene


>>> sentido que el "expect" tenga un valor definido. (En este caso, los
>>> productos son stubs y solo me interesa lo que devuelven)
>>>
>>>
>>> A ver si me aclaro. En este caso, el colaborador "pasarela de pago"
>>> es un mock y por eso tiene sentido que especifiquemos lo que recibe,
>>> ya que es justo lo que queremos probar. En el caso del anterior
>>> mensaje, el colaborador "pasarela de pago" es un stub y y me da
>>> igual lo que reciba porque solo me interesa definir su
>>> comportamiento (que acepta el pago). �Ayuda esto en algo?
>>>
>>>
>>> Cuando a un colaborador del que solo nos interesa el resultado de su

>>> colaboraci�n (stub) le definimos los argumentos de entrada creo que,


>>> en cierto modo, estamos creando un "expect", ya que nuestro test
>>> solo va a funcionar SI Y SOLO SI el stub recibe dicho argumento de
>>> entrada. Es decir, el colaborador actua como mock y como stub a la

>>> vez, dej�ndonos con el culo al aire. A ver si aclaro un poco mis


>>> ideas y escribo un post sobre esto :P
>>>
>>>
>>> Disclaimer:

>>> Cuando digo �ltimamente es hace 3 meses o as� (hasta donde me llega


>>> la memoria :D), por lo que tampoco hay que tomarme muy en serio. De
>>> todas formas, visto lo visto, probablemente dentro de unos meses

>>> dire algo as� como "por supuesto que hay que pasar los argumentos a
>>> los stubs. Siempre lo he hecho as� y creo que no hacerlo as� es un


>>> olor" :P
>>>
>>>
>>> Un saludo
>>>
>>> --

>>> Alberto Pe�a Abril

>>> Has recibido este mensaje porque est�s suscrito al grupo "TDDev" de
>>> Grupos de Google.
>>> Para publicar una entrada en este grupo, env�a un correo electr�nico
>>> a tdde...@googlegroups.com.
>>> Para anular tu suscripci�n a este grupo, env�a un correo electr�nico
>>> a tddev-sp+u...@googlegroups.com
>>> Para tener acceso a m�s opciones, visita el grupo en
>>> http://groups.google.com/group/tddev-sp?hl=es.
>>>
>> --
>> Has recibido este mensaje porque est�s suscrito al grupo "TDDev" de
>> Grupos de Google.
>> Para publicar una entrada en este grupo, env�a un correo electr�nico a
>> tdde...@googlegroups.com.
>> Para anular tu suscripci�n a este grupo, env�a un correo electr�nico a
>> tddev-sp+u...@googlegroups.com
>> Para tener acceso a m�s opciones, visita el grupo en
>> http://groups.google.com/group/tddev-sp?hl=es.
>

Jorge Uriarte Aretxaga

unread,
Feb 3, 2011, 4:27:01 AM2/3/11
to tdde...@googlegroups.com
Para mí la clave es que el stub es una microimplementación suficiente
como para contener una parte del estado del objeto real, y de emular
una parte de su comportamiento. En esos casos te puedes encontrar con
que tu objeto probado debe realizar varias inteacciones con su
colaborador (el que va a seré stub) y este contestar diferente en
función de la llamada. Ahí cobra sentido por economía o por
consistencia del test, que el stub tenga una micrológica para emular
su comportamiento real.
Dicen que no hay mejor mentira que una verdad. En esa linea, el mejor
stub posible sería el objeto colaborador real.

Todo esto con el warning que ha dado Alfredo (creo). Si estás haciendo
un stub complicado, si empiezas a complicar la lógica de respuesta...
¿Puede que tu test sea demasiado ambicioso? No hay respuestas
enlatadas, todas hay que currárselas ;)

Saludos,

Jorge

On Thursday, February 3, 2011, mariotux <ma...@pensandoenred.com> wrote:
> Buenas,
>
> Yo sigo sin entender el porqué de que un stub tenga que entender de
> parámetros. Lo que yo entiendo de un stub, es que deseamos que cuando se
> invoque a un método X nos retorne el valor Y.
>
> Si queremos controlar los parámetros usaríamos un Mock? Todavía no me he
> metido mucho con esta parte pero, entiendo que el Mock es para controlar
> si la invocación al método se realiza y con que parámetros.
>
> No acabo de ver el poder stubear un método aplicándole una lógica.
>


--

_
Jorge Uriarte Aretxaga
http://www.gailen.es
http://www.linkedin.com/in/jorgeuriarte

Luis Artola

unread,
Feb 3, 2011, 4:37:37 AM2/3/11
to tdde...@googlegroups.com
Hola chicos,

soy Luis Artola (@programania) me parece que es la primera vez que escribo en la lista. El problema del cajero que se conecta a un gateway externo es viene de una kata creada por Guillermo y que intentamos resolver juntos. Allí nos surgió la duda ésta del stub.

Nos pasa una cosa: gracias a la Code Katas vamos entendiendo el TDD y acercándonos al objetivo final: utilizarlo en desarrollos reales y no en katas. Pero no hemos encontrado katas para practicar stubs y mocks. Que se me antojan totalmente necesarios para hacer "TDD real".

¿Alguna recomendación de Kata-ejercicio-whatever para practicar stubbing y mocking?

Un saludote para todos!



2011/2/3 Jorge Uriarte Aretxaga <jorge....@gailen.es>
--
Has recibido este mensaje porque estás suscrito al grupo "TDDev" de Grupos de Google.
Para publicar una entrada en este grupo, envía un correo electrónico a tdde...@googlegroups.com.
Para anular tu suscripción a este grupo, envía un correo electrónico a tddev-sp+u...@googlegroups.com
Para tener acceso a más opciones, visita el grupo en http://groups.google.com/group/tddev-sp?hl=es.




--
Luis Artola
"Larga vida a la carne nueva"

http://www.programania.net
http://www.precriticas.com

José Manuel Beas

unread,
Feb 3, 2011, 7:55:01 PM2/3/11
to tdde...@googlegroups.com
Luis, quizás te interese ésta.
Fíjate en la última linea: "It’s also an interesting exercise in testing: can you write unit tests to verify that your code is working correctly before setting it to work on the full dictionary."

Luis Artola

unread,
Feb 4, 2011, 1:33:50 AM2/4/11
to tdde...@googlegroups.com

Gracias JM... Por ahi van los tiros! 

Carlos Ble

unread,
Feb 5, 2011, 1:43:26 PM2/5/11
to tdde...@googlegroups.com
Steve Freeman dice, usa stubs para consultas y mocks para acciones. No hay que darle muchas mas vueltas :-), aunque a veces uso mocks para consultas ;-)

El 3 de febrero de 2011 09:05, Guillermo Gutiérrez <ggal...@gmail.com> escribió:
Buenas!

Aunque no he tenido tiempo de estudiarlo con detenimiento (tiene muchísima miga), he leído las definiciones de Test Stub y Fake Object de xunitpatterns.com. No conocía la web y la cantidad de literatura que hay al respecto es abrumadora.

Definición de un Test Double: http://xunitpatterns.com/Test%20Double.html
Definición de un Test Stub: http://xunitpatterns.com/Test%20Stub.html
Definición de un Fake Object: http://xunitpatterns.com/Fake%20Object.html

En principio no parece estar explícitamente definido si un stub debe devolver el mismo valor o distintos valores. Volviendo a la justificación de todo este tinglado, parece que lo que importa es poder usar *un doble* para sustituir un componente que está fuera de nuestro contexto de testeo. Luego, en función de aquello que estemos sustituyendo por un doble, tendremos que optar por uno u otro tipo de implementación: desde clases hechas a mano a tal efecto, hasta usando clases definidas dinámicamente con frameworks como jmock, mockito, etc.

Pienso que toda la confusión que tengo en este sentido se debe a cómo están implementados los frameworks de mockeo en PHP, ya que ninguno permite distintos valores de retorno en función de ciertas condiciones. jMock, por ejemplo, permite esto de fábrica por medio de dos mecanismos: distintos valores en llamadas consecutivas y condicionadores de retorno tipo "with(blablabla).returnValue(blablabla)". Por no hablar de la libertad que tenemos de crear una clase a mano que haga literalmente lo que nos dé la gana.

Insisto, todo esto sin tener en cuenta si es lo más apropiado o no a la hora de definir nuestros tests unitarios.


On 03/02/11 08:33, mariotux wrote:
Buenas,

Yo sigo sin entender el porqué de que un stub tenga que entender de
parámetros. Lo que yo entiendo de un stub, es que deseamos que cuando se
invoque a un método X nos retorne el valor Y.

Si queremos controlar los parámetros usaríamos un Mock? Todavía no me he

metido mucho con esta parte pero, entiendo que el Mock es para controlar
si la invocación al método se realiza y con que parámetros.

No acabo de ver el poder stubear un método aplicándole una lógica.


Si stubeo un colaborador, entiendo que cuando la clase que estoy
testeando vaya a invocar el método de ese colaborador yo espero que

retorne Y para el test que estoy haciendo.

Si quiero comprobar la lógica del método de ese colaborador, el mismo,
tendrá sus tests.

Para que aplicar una lógica en el stub?


Pero como he comentado en el inicio de este hilo, soy novato y estamos
aprendiendo :-) Iluminarnos Maestros del TDD ^_^

Salu2.

Mario Nunes

El mié, 02-02-2011 a las 08:11 +0100, Guillermo escribió:
Muchas gracias por vuestras respuestas.


Las conclusiones entonces parecen las siguientes:


1) salvo en casos excepcionales, es preferible reorientar el test
hacia otro tipo de prueba
2) tiene sentido que un stub pueda devolver un valor distinto en
función de parámetros



Claramente, en mi ejemplo, lo que procede es reorientar el test.


Perdonad la brevedad del mensaje, pero escribo de camino al curro :P


Guille
Enviado desde mi iPhone

El 02/02/2011, a las 02:42, Alberto Peña<plag...@gmail.com>
escribió:



El 2011/2/2 Alfredo Casado<casado....@gmail.com>  dijo
        Pues si, tienes razón y el test tal y como esta prueba más
        cosas de las que dice, además de probar que el cliente si

        tiene fondos puede realizar la compra prueba "de paso" que
        la compra realiza correctamente el calculo del precio.


        Es un olor de diseño porque el calculo de la compra o bien
        esta en algún colaborador que contenga la estrategia del
        calculo de compra o bien puede ser un método público de la
        compra, no necesito comprobarlo en ese test y lo único que
        consigo haciendo eso es atar más el test a la
        implementación.




A eso iba. Normalmente, cuando especificamos los valores de entrada
es porque estamos comprobando diferentes comportamientos con un solo
test.




        Era un ejemplo para demostrar el uso de stubs y mocks en el
        mismo método y no se me ocurrió hacerlo mejor, pero es un
        mal diseño y ese test lo demuestra. No se me ocurre ningún

        caso ahora mismo donde sea imprescindible definir lo que
        devuelve el stub en función del parametro de entrada, pero

        de eso a decir que es un mal olor siempre... tampoco estoy
        seguro porque es un mal olor programar así los stubs, en

        principio no veo que tenga nada "intrinsecamente" malo.



No se, puede ser que no sea un olor, pero es algo que, al menos
últimamente :P, me dice que estoy haciendo  algo mal. Más que nada

por lo que has dicho antes ( y que ya he repetido :D). Me da la
impresión de que intentamos probar varias cosas a la vez. Además,

los argumentos de entrada que reciben los colaboradores de una clase
es un "detalle de implementación" que acopla el test al código. Esto
es así.

Otra cosa es que usemos mocks y hagamos algo como esto:


 @Test public void


    al_comprar_llamamos_a_la_pasarela_de_pago_con_el_valor_total_de_la_compra() {


       when(unProductoStub.precio()).thenReturn(50);

       when(otroProductoStub.precio()).thenReturn(25);



       compra.anadir(unProductoStub);

       compra.anadir(otroProductoStub);


       compra.confirmar();



      expect(pasarelaDePagoStub.tieneElUsuarioFondosPorValorDe(75));



    }



En este caso, estamos probando algo muy diferente y sí que tiene

sentido que el "expect" tenga un valor definido. (En este caso, los
productos son stubs y solo me interesa lo que devuelven)


A ver si me aclaro. En este caso, el colaborador "pasarela de pago"
es un mock y por eso tiene sentido que especifiquemos lo que recibe,
ya que es justo lo que queremos probar. En el caso del anterior
mensaje, el colaborador "pasarela de pago" es un stub y y me da
igual lo que reciba porque solo me interesa definir su
comportamiento (que acepta el pago). ¿Ayuda esto en algo?


Cuando a un colaborador del que solo nos interesa el resultado de su
colaboración (stub) le definimos los argumentos de entrada creo que,

en cierto modo, estamos creando un "expect", ya que nuestro test
solo va a funcionar SI Y SOLO SI el stub recibe dicho argumento de
entrada. Es decir, el colaborador actua como mock y como stub a la
vez, dejándonos con el culo al aire. A ver si aclaro un poco mis

ideas y escribo un post sobre esto :P


Disclaimer:
Cuando digo últimamente es hace 3 meses o así (hasta donde me llega

la memoria :D), por lo que tampoco hay que tomarme muy en serio. De
todas formas, visto lo visto, probablemente dentro de unos meses
dire algo así como "por supuesto que hay que pasar los argumentos a
los stubs. Siempre lo he hecho así y creo que no hacerlo así es un

olor" :P


Un saludo

--
Alberto Peña Abril
Has recibido este mensaje porque estás suscrito al grupo "TDDev" de
Grupos de Google.
Para publicar una entrada en este grupo, envía un correo electrónico
a tdde...@googlegroups.com.

Para anular tu suscripción a este grupo, envía un correo electrónico
a tddev-sp+u...@googlegroups.com
Para tener acceso a más opciones, visita el grupo en
http://groups.google.com/group/tddev-sp?hl=es.

--
Has recibido este mensaje porque estás suscrito al grupo "TDDev" de
Grupos de Google.
Para publicar una entrada en este grupo, envía un correo electrónico a
tdde...@googlegroups.com.
Para anular tu suscripción a este grupo, envía un correo electrónico a
tddev-sp+u...@googlegroups.com
Para tener acceso a más opciones, visita el grupo en
http://groups.google.com/group/tddev-sp?hl=es.


--
Has recibido este mensaje porque estás suscrito al grupo "TDDev" de Grupos de Google.
Para publicar una entrada en este grupo, envía un correo electrónico a tdde...@googlegroups.com.
Para anular tu suscripción a este grupo, envía un correo electrónico a tddev-sp+u...@googlegroups.com
Para tener acceso a más opciones, visita el grupo en http://groups.google.com/group/tddev-sp?hl=es.




--

Alberto Peña

unread,
Feb 5, 2011, 1:52:46 PM2/5/11
to tdde...@googlegroups.com
Hombre Carlos, yo también soy "fan" de Steve Freeman y Nat Pryce (vamos, del goos), pero no está de más entender por qué lo dicen :P

Un saludo

2011/2/5 Carlos Ble <ble.j...@gmail.com>

Carlos Ble

unread,
Feb 5, 2011, 2:10:46 PM2/5/11
to tdde...@googlegroups.com
Bueno la experiencia te va diciendo qué tests con más frágiles y demas :-)
Hay que practicar y practicar, es lo que les diria yo :-)

Enrique Amodeo

unread,
Feb 5, 2011, 2:28:55 PM2/5/11
to tdde...@googlegroups.com
Yo uso mocks para testear el protocolo de comunicaciones entre el
objeto que estás probando y sus colaboradores. Los stubs los uso
cuando realmente este protocolo no me interesa y estoy concentrado en
el resultado del método, que depende de los resultados de los métodos
de los stubs.

La mayor parte de las veces mezclo las dos cosas. En general me
interesa el patrón de llamadas que se hace a los colaboradores cuando
invoco un método, y a la vez el resultado del método que suele
depender de lo que devuelvan estos. Por lo tanto en mi caso mezclo
mocks con stubs y no me preocupo por hacer distinciones teóricas.

Lo importante es que el test sea tan legible o más que el código de
producción, y que no sea frágil. Por frágil me refiero a que se acople
a los detalles de implementación de un método, y en cuanto cambias
algún detalle irrelevante de este rompes el test. Para mi lo más
difícil de testear con mocks es que tus tests no se vuelvan frágiles.
Cosas como forzar al mock a que deba recibir llamadas en determinado
orden cuando en realidad no es necesario o comprobar con un nivel de
detalle irrelevante los parámetros de las llamadas, o el número de
llamadas recibido por cada métodos.

Salud !


2011/2/5 Carlos Ble <ble.j...@gmail.com>:

alberto rodriguez

unread,
Feb 5, 2011, 8:36:38 PM2/5/11
to TDDev
Si no habéis encontrado katas es que no habéis mirado bien ;). Las
katas no te imponen una implementación, así que cualquier kata te
vale, mientras en tu implementación añadas al menos un colaborador.
Sin ir más lejos, yo usé varios colaboradores en alguna de las
implementaciones del String Calculator.

On Feb 3, 10:37 am, Luis Artola <luisarto...@gmail.com> wrote:
> Hola chicos,
>
> soy Luis Artola (@programania) me parece que es la primera vez que escribo
> en la lista. El problema del cajero que se conecta a un gateway externo es
> viene de una kata creada por Guillermo y que intentamos resolver juntos.
> Allí nos surgió la duda ésta del stub.
>
> Nos pasa una cosa: gracias a la Code Katas vamos entendiendo el TDD y
> acercándonos al objetivo final: utilizarlo en desarrollos reales y no en
> katas. *Pero no hemos encontrado katas para practicar stubs y mocks*. Que se
> me antojan totalmente necesarios para hacer "TDD real".
>
> ¿Alguna recomendación de Kata-ejercicio-whatever para practicar stubbing y
> mocking?
>
> Un saludote para todos!
>
> 2011/2/3 Jorge Uriarte Aretxaga <jorge.uria...@gailen.es>
> > tddev-sp+u...@googlegroups.com<tddev-sp%2Bunsubscribe@googlegroups.c om>
> > Para tener acceso a más opciones, visita el grupo en
> >http://groups.google.com/group/tddev-sp?hl=es.
>
> --
> Luis Artola
> *"Larga vida a la carne nueva"*
>
> http://www.programania.nethttp://www.precriticas.com

Luis Artola

unread,
Feb 6, 2011, 2:58:27 AM2/6/11
to tdde...@googlegroups.com
Gracias Alberto. Echare un vistazo a tu implementación!

El 06/02/2011, a las 02:36, alberto rodriguez
<albert...@gmail.com> escribió:

Reply all
Reply to author
Forward
0 new messages