Dudas test unitarios

16 views
Skip to first unread message

Jesus Jimenez

unread,
Jul 22, 2010, 6:34:26 AM7/22/10
to TDDev
Hola,

Estoy creando los test de una aplicación y como siempre lo (poco) que
he hecho son test de integración, a la hora de ponerme a hacer
unitarios, me están surgiendo algunas dudas que me gustaría que
alguien me pudiese aclarar. La aplicación está desarrollada en Java
usando Spring 2.5 y Struts 1.3 (aunque de esto todavía no he empezado
con los test). Además, la aplicación ataca a una base de datos y a un
LDAP para obtener (y guardar en el caso de la BD) distinta
información. Para hacer los test estoy usando JUnit 4.4 y para todo el
tema de los mocks estoy con Mockito, además de tener pensado usar
dbunit para el test de los daos cuando empiece. Comento esto por si
puede ayudar a la hora de responderme a las preguntas que haga.

Bueno, primero decir que es posible que tenga errores (gordos) de
concepto, no me fusteguis mucho por ello ;) De momento no uso TDD
porque primero tengo que "alcanzar" con los test al desarrollo ya
hecho (y aprender como hacer buenos test), pero la idea es que cuando
termine de hacer los test para todo lo que hay, continuar con el
desarrollo usando TDD en lo que mi conocimiento me permita.

* ¿Los test unitarios se deben de hacer dentro del contexto de Spring
o fuera de él? Entiendo que fuera, que debo ser yo quien "inyecte" los
beans que necesite, pero prefiero confirmar.

* ¿Cómo afrontar los test unitarios de los DAOs? ¿Con conexión a LDAP
y BD o sin esa conexión? Como entiendo que no debería usar el LDAP y
BD "reales", no tengo claro como gestionar esto, si hacer mocks y en
caso de que sí, cómo hacerlo.

* Un caso en concreto. En los test unitarios lo que estoy haciendo es
en los diferentes test ver si el resultado devuelto es el correcto y
si ha pasado por donde yo quería que pasara (a través de los verify de
Mockito). Entiendo que esto es lo que hay que hacer (o igual esto es
erróneo porque necesito conocer cómo está hecho el método que estoy
probando), pero tengo un problema en algunos momentos para comprobar
que ha pasado por donde debería, ya que necesito saber si se le ha
cambiado un atributo a un bean a partir de una cierta información.
Sería sencillo ver que se ha hecho el set del bean que le paso como
parámetro a la función, pero el caso es que ese bean al principio del
método se ha transformado en otro, pongo un ejemplo:

public void metodo(MiBean bean, boolean cambiarValor){
MiNuevoBean beanTransformado = tranformarEnNuevoBean(bean);
if (cambiarValor){
beanTransformado.setNuevoValor(X);
}
miDAO.guardar(beanTransformado);
}


* En el ejemplo anterior también tengo el problema de que no puedo (o
más concretamente, no se) como verificar que se ha llamado a
miDAO.guardar, ya que al haber transformado el bean, no puedo hacer
verify(miMockDAO,times(1)).guardar(nuevoBean)ya que yo no tengo en el
test el nuevoBean porque se ha creado dentro (no se si me explico...)

Y de momento nada más!!! Muchas gracias de antemano.

Un saludo.
Jesús.

Alfredo Casado

unread,
Jul 22, 2010, 9:14:08 AM7/22/10
to tdde...@googlegroups.com
No uso spring, pero los test unitarios que traten a las clases como clases si estar dentro de ningún contexto ni cosas parecidas.

Sobre el problema con el test que comentas, tienes;

MiNuevoBean beanTransformado = tranformarEnNuevoBean(bean);

En lugar de tener una función tranformarEnNuevoBean puedes tener un colaborador que se encargue de estas transformaciones, el "TransformadorDeBeans" de forma que tendrías algo como:

MiNuevoBean beanTransformado = transformadorDeBeans.tranformarEnNuevoBean(bean);

De esta forma ahora puedes mockear el transformador de beans y los beans que este devuelva y hacer todas las comprobaciones que necesitas.

De paso toda la lógica de "transformar beans", sea lo que sea, queda aislada en una clase que se encarga sólo de esas transformaciones que no parecen encajar mucho como un método privado de una clase que se encargue de otras cosas.


--
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.


Jesus Jimenez

unread,
Jul 22, 2010, 10:11:13 AM7/22/10
to tdde...@googlegroups.com
No puse bien el código, realmente hay una clase con métodos estáticos que me devuelven las transformaciones. Es decir, más o menos lo que comentas (pero lo que tu dices no son métodos estáticos). ¿Debería de hacer que la clase que quiero probar reciba este objeto desde fuera con un setTransformador en vez de hacerlo la misma clase con un MiTransformador.transformar(XXX) de cara a poder pasarle un mock? ¿Hasta que punto debo modificar este tipo de cosas en una clase para que pueda testear la clase?

Estas transformaciones se hacen de cara a que los beans que se usan en pantalla no sean los mismos que se recogen/guardan en la BD. Quizás no sea la mejor solución pero es como está montado (por el momento).

Muchas gracias Alfredo.

Un saludo.

José Manuel Beas

unread,
Jul 22, 2010, 10:18:11 AM7/22/10
to tddev-sp
+1 Alfredo
-1 a métodos estáticos

El problema con el DAO es doble:
- tu método "metodo" está haciendo tres cosas (transformando, decidiendo y guardando)
- el DAO es un colaborador del objeto que tiene el "metodo", con lo que podrías tener otro mock para éste

Al separar las responsabilidades puedes hacer pruebas de integración específicas o delegar las responsabilidades en otros objetos (en este caso el DAO) con sus correspondientes pruebas unitarias (aunque esto nos llevara a la recurrente discusión sobre si las pruebas de un DAO son unitarias o de integración).

Por último, y a riesgo de resultar pedante: el hecho de que el código esté hecho así y que encima funcione no quiere decir que esté bien diseñado. Si estás dispuesto a convivir con un código difícil (o excesivamente costoso) de testear, es una decisión aceptable pero que tiene un nombre: "deuda técnica". Recuerda que todas las deudas, con el tiempo, aumentan los intereses que debes pagar... ;-)

Un saludo,
Jose Manuel Beas



Alfredo Casado

unread,
Jul 22, 2010, 10:30:14 AM7/22/10
to tdde...@googlegroups.com
¿Debería de hacer que la clase que quiero probar reciba este objeto desde fuera con un setTransformador en vez de hacerlo la misma clase con un MiTransformador.transformar(XXX) de cara a poder pasarle un mock?

Justo eso, ahora mismo tienes una dependencia estática que hace que sea imposible probar tu clase sin probar a la vez el transformador, además si usáis spring!, tenéis inyección de dependencias, no hagáis clases de utilidad con métodos estáticos, inyectaros objetos colaboradores y tendréis no sólo un sistema más testable sino también más flexible para futuros cambios.

¿Hasta que punto debo modificar este tipo de cosas en una clase para que pueda testear la clase?

Depende de lo que modifiques, por lo general los test sólo te piden buen diseño, pero tampoco cometas atrocidades por hacer tus clases más fáciles de testear, no existe "un punto limite" es una cuestión de sentido común, buen diseño y de analizar cada caso.

--

Jesus Jimenez

unread,
Jul 22, 2010, 10:31:23 AM7/22/10
to tdde...@googlegroups.com
Veo que no he terminado de explicar bien la estructura que hay...

Hay unas clases "servicios" que reciben un bean de los Action de Struts, estos servicios transforma este bean a través de los métodos estáticos, deciden qué hay que hacer con los datos, y le pasa a los DAOs los datos que necesitan para realizar sus operaciones. Las pruebas que estoy haciendo es probar los servicios de la forma más independiente posible, por supuesto usando mocks para los DAOs. Luego la idea es probar también de forma independiente los DAOs y luego ya hacer las pruebas de integración con los 2 juntos.

Me parece bien que este servicio reciba un objeto del tipo "transformador" y así le puedo pasar mocks y tiene más sentido que métodos estáticos... apuntado!!

Y tienes toda la razón en lo referente a la deuda técnica y tengo claro que la aplicación no está bien diseñada (de lo cual tengo yo bastante culpa). Metiendo los test aparte del objetivo claro de que la aplicación tenga test para probarse en condiciones, también tenía como objetivo encontrar este tipo de problemas y que me ayude a mejorar el diseño. El problema que tengo es que el jefe me sigue exigiendo avances en la aplicación (es decir, más ventanitas y más funcionalidad) por lo que los cambios de diseño los voy haciendo poco a poco. No significa que me conforme porque funciona, sino que no hay tiempo para todo.

Gracias!!!


Jesus Jimenez

unread,
Jul 22, 2010, 10:36:34 AM7/22/10
to tdde...@googlegroups.com
Justo eso, ahora mismo tienes una dependencia estática que hace que sea imposible probar tu clase sin probar a la vez el transformador, además si usáis spring!, tenéis inyección de dependencias, no hagáis clases de utilidad con métodos estáticos, inyectaros objetos colaboradores y tendréis no sólo un sistema más testable sino también más flexible para futuros cambios.

Es que tengo una manía que tengo que quitarme a la de ya, y es usar estas clases de utilidad estáticas porque me facilitan la vida, pero salvo excepciones creo que no son demasiado aconsejables...


Depende de lo que modifiques, por lo general los test sólo te piden buen diseño, pero tampoco cometas atrocidades por hacer tus clases más fáciles de testear, no existe "un punto limite" es una cuestión de sentido común, buen diseño y de analizar cada caso.

No, a estas alturas no voy a desmontar la aplicación para que se pueda testear jeje. Pero vamos, que entiendo que cambiar determinadas cosas como las que hemos comentado no solo es aconsejable sino casi obligatorio!


José Manuel Beas

unread,
Jul 22, 2010, 10:48:50 AM7/22/10
to tddev-sp
On 22 July 2010 16:31:23 UTC+2, Jesus Jimenez <yey...@gmail.com> wrote:
Veo que no he terminado de explicar bien la estructura que hay...


Usar patrones de diseño ayuda a comunicar. :-)

Hay unas clases "servicios" que reciben un bean de los Action de Struts, estos servicios transforma este bean a través de los métodos estáticos, deciden qué hay que hacer con los datos, y le pasa a los DAOs los datos que necesitan para realizar sus operaciones. Las pruebas que estoy haciendo es probar los servicios de la forma más independiente posible, por supuesto usando mocks para los DAOs. Luego la idea es probar también de forma independiente los DAOs y luego ya hacer las pruebas de integración con los 2 juntos.


Entiendo que los beans que las Action están haciendo llegar a tus servicios son ActionForms o algo parecido, es decir, DTOs, meros contenedores de datos... y que los tratas de transformar en otros DTOs para que los DAOs los puedan guardar en la BD. Creo que hemos leido el mismo libro... y estás sufriendo las mismas consecuencias que yo sufrí por esta (pésima) arquitectura. Es mucho mejor pasar de DTOs y hacer que las Actions llamen a los servicios con métodos menos genéricos y más semánticos. Te evitas transformaciones genéricas que, en realidad, están ocultando muchas reglas de negocio y te impiden encontrar mejores diseños y refactorizaciones.

  
Me parece bien que este servicio reciba un objeto del tipo "transformador" y así le puedo pasar mocks y tiene más sentido que métodos estáticos... apuntado!!


Y recuerda que si tienes que inyectar demasiados colaboradores, tu diseño también tiene un problema. Eso es algo que Spring te oculta cuando haces el autowire pq no cuesta casi nada inyectar un nuevo colaborador.
 
Y tienes toda la razón en lo referente a la deuda técnica y tengo claro que la aplicación no está bien diseñada (de lo cual tengo yo bastante culpa). Metiendo los test aparte del objetivo claro de que la aplicación tenga test para probarse en condiciones, también tenía como objetivo encontrar este tipo de problemas y que me ayude a mejorar el diseño. El problema que tengo es que el jefe me sigue exigiendo avances en la aplicación (es decir, más ventanitas y más funcionalidad) por lo que los cambios de diseño los voy haciendo poco a poco. No significa que me conforme porque funciona, sino que no hay tiempo para todo.


Si yo fuera tu jefe te pediría lo mismo. Y haces lo mejor que se puede hacer. Poco a poco ir haciendo cambios de diseño. Lo mejor es tratar de hacer los cambios de diseño en las nuevas funcionalidades. Si las viejas están funcionando no parece urgente rediseñarlas, sin embargo, las nuevas (incluidas las que tienes que tocar pq alguien se ha quejado de un defecto) son las ideales para ir creando tu red de seguridad. Eso sí, si no tienes aire acondicionado en tu casa, el verano es un momento ideal para refactorizarlo todo: hay pocos compañeros que te puedan culpar y tienes mucho tiempo libre. (Era sarcasmo, ¡eh!)
 
Un saludo,
JMB

Jesus Jimenez

unread,
Jul 22, 2010, 11:04:43 AM7/22/10
to tdde...@googlegroups.com
Entiendo que los beans que las Action están haciendo llegar a tus servicios son ActionForms o algo parecido, es decir, DTOs, meros contenedores de datos... y que los tratas de transformar en otros DTOs para que los DAOs los puedan guardar en la BD. Creo que hemos leido el mismo libro... y estás sufriendo las mismas consecuencias que yo sufrí por esta (pésima) arquitectura. Es mucho mejor pasar de DTOs y hacer que las Actions llamen a los servicios con métodos menos genéricos y más semánticos. Te evitas transformaciones genéricas que, en realidad, están ocultando muchas reglas de negocio y te impiden encontrar mejores diseños y refactorizaciones.


No necesito leer ningún libro para hacer las cosas mal!!! es cosecha propia jejeje.
No entiendo a lo que te refieres con métodos menos genéricos y más semánticos, ¿algún ejemplo sencillito? La verdad es que este modo de diseño es un infierno y además acabo haciendo mucho "copy&paste" lo cual me indica que no es demasiado bueno...


Eso sí, si no tienes aire acondicionado en tu casa, el verano es un momento ideal para refactorizarlo todo: hay pocos compañeros que te puedan culpar y tienes mucho tiempo libre. (Era sarcasmo, ¡eh!)

Tengo un aire acondicionado estupendo en casa :)

José Manuel Beas

unread,
Jul 22, 2010, 12:21:08 PM7/22/10
to tdde...@googlegroups.com
Dame un par de ejemplos de llamadas a tus servicios y trataré de
explicarte lo que quiero decir. Estoy recién levantando de la siesta.
:P


--

Alberto Peña

unread,
Jul 22, 2010, 5:15:17 PM7/22/10
to tdde...@googlegroups.com
¡Hola chicos! Como buen fan de TDD voy a decirle a Jesus: "Eso te pasa por no hacer TDD" :P

Hace poco vi una presentación de Misko Hevery en la que decía que no se trata de hacer "buenos tests", se trata de hacer código fácil de testear. Quicir, si tienes que cambiar tu código, cámbialo. Dentro de unos meses lo agradeceras.

A nosotros nos costó mucho entrar en vereda. ¡Alfredo!, dile como son los tests de hace un par de años. No te lo va a decir porque el muy perro se ha ido hoy de vacaciones, pero ya te lo digo yo. Una basura. En serio, no los tiramos por vergüenza (y porque no tenemos pelotas). Nos pasaba igual que a ti pero sin inyección de dependencias y con easymock en lugar de mockito. Empezábamos tirando código y luego haciendo tests. ¿Que pasaba? Que nos parecía imposible hacer buenos tests porque nuestras clases no se dejaban testear, que nos costaba un riñon poner el objeto a testear en el estado que necesitabamos haciendo que los test ocuparan ¡más de una pantalla!, que los mocks duplicaban el código porque no sabíamos usarlos haciendo que cualquier cambio se cargara los tests, etc... Vamos, que nos hemos dado hostias a saco por no hacer código fácilmente testeable. Ahora ya vamos aprendiendo. No se muy bien que pasó, pero creo que fue el empezar a hacer TDD. En serio, personalmente TDD me ha hecho hacer un código mucho mejor de lo que hacía antes. Pero mucho, mucho.

Mi sensación es que a ti te está pasando lo mismo :P Pero no te preocupes, porque ya estás haciendo lo más difícil. Si no te rindes,y no te rindas porque cualquier otro camino es un camino erroneo (Ahora vendrá el Beas a decirme que depende de la pasta... Que no te engañe :P), dentro de poco empezaras a verlo todo un poco más claro. Te darás cuenta de que los estáticos son el mal, de que la inyección de dependencias y los tests son como las fresas y la nata... En realidad todo es mucho más sencillo de lo que parece, solo se trata de hacerlo en el orden correcto :P

Un saludo taliban.

Disclaimer: Que conste que el mensaje está un poco exagerado, pero me habéis pillado nostálgico ¿Será por que mañana me voy a ver a Belle & Sebastian a Santander?

--
Alberto Peña Abril

http://twitter.com/plagelao
http://plagelao.blogspot.com/

Jesus Jimenez

unread,
Jul 22, 2010, 6:11:58 PM7/22/10
to tdde...@googlegroups.com
Alberto, tienes razón, pero no podía aplicar TDD desde el principio por 2 motivos importantes. Primero porque no se hacer TDD (ni como he demostrado un conjunto de test decentes) y no puedo hacerle pagar al cliente (al que acababa de llegar) mi aprendizaje y segundo porque desde el principio querían "ir viendo algo" y de hacer TDD iría muy lento al principio. Todavía no me había casi ni terminado de montar el entorno de desarrollo y ya me estaban preguntando si tenían algo para ver...

Y no creo que dentro de unos meses agradezca nada, el jueves que viene me mandan a otro proyecto que empezaré desde 0, y ahí sí que me gustaría aplicar TDD.

Un saludo y gracias a todos :)

Edra

unread,
Jul 24, 2010, 7:43:32 AM7/24/10
to TDDev
Hola Jesus:

Aunque ya habéis hablado bastante del tema y de cómo conseguir un
código más "TDD", no sé si has podido encontrar alguna solución para
probar el código en su estado actual.

En el ejemplo que has expuesto en el primer posts, sí que existen
formas de probar el contenido del Pojo que has creado dentro de tu
método: en JMock se llaman "Matchers" (en Mockito no sé como se
llamarán, pero tiene que haber algún equivalente).

Por ejemplo, si tengo el siguiente método (el "transform", igual que
el tuyo, puede ser estático, privado, lo que sea...):

public void firstMethod(FirstPojo firstPojo, boolean change) {
FirstPojo newPojo = transform(firstPojo);
if (change) {
newPojo.setFirstField("test");
}
firstDao.save(newPojo);
}

Usando JMock, una manera de probar la llamada correcta al "save()" y
el contenido esperado de "newPojo" sería:

@Test
@SuppressWarnings({ "unchecked", "rawtypes" })
public void testFirstMethodShouldUpdateFirstFieldWhenInputIsTrue() {
mockery.checking(new Expectations() {
{
Matcher m1 = Matchers.hasProperty("firstField",
Matchers.equalTo("test"));
Matcher m2 = Matchers.hasProperty("secondField",
Matchers.equalTo("b"));
Matcher<FirstPojo> matcher = Matchers.allOf(m1, m2);
exactly(1).of(firstDaoMock).save(with(matcher));
}
});

FirstPojo pojo = new FirstPojo();
pojo.setFirstField("a");
pojo.setSecondField("b");
firstService.firstMethod(pojo, true);
}

Como puedes ver, aunque la sintaxis es algo liosa, los "Matchers"
son muy útiles para comprobar el estado del objeto, independientemente
de cuál sea su origen, ya que sólo se definen las condiciones que debe
cumplir.

Espero que te sirva de ayuda.

Un saludo,

--
Edraí Brosa


On Jul 23, 12:11 am, Jesus Jimenez <yeyu...@gmail.com> wrote:
> Alberto, tienes razón, pero no podía aplicar TDD desde el principio por 2
> motivos importantes. Primero porque no se hacer TDD (ni como he demostrado
> un conjunto de test decentes) y no puedo hacerle pagar al cliente (al que
> acababa de llegar) mi aprendizaje y segundo porque desde el principio
> querían "ir viendo algo" y de hacer TDD iría muy lento al principio. Todavía
> no me había casi ni terminado de montar el entorno de desarrollo y ya me
> estaban preguntando si tenían algo para ver...
>
> Y no creo que dentro de unos meses agradezca nada, el jueves que viene me
> mandan a otro proyecto que empezaré desde 0, y ahí sí que me gustaría
> aplicar TDD.
>
> Un saludo y gracias a todos :)
>
> > tddev-sp+u...@googlegroups.com<tddev-sp%2Bunsu...@googlegroups.com>

José Manuel Beas

unread,
Jul 24, 2010, 7:58:25 PM7/24/10
to tdde...@googlegroups.com
IMHO, cuando un diseño es difícil de probar suele ser señal de que no
es un buen diseño. Yo, personalmente, no invertiria tanto en tests
tan acoplados a la implementación y sí más en ir cambiando el diseño
hacia un modelo menos anémico, que incluso nos lleva a llamar objeto
(pojo) a un simple bean para transportar datos entre capas. ;)

Un saludo,
JMB


--

Reply all
Reply to author
Forward
0 new messages