Test que acceden a la BD. (Era: Buena metodología para test unitario.)

198 views
Skip to first unread message

Pablo Braulio

unread,
Nov 2, 2011, 6:51:57 AM11/2/11
to Lista Agile-spain
Hola a todos.

Hace cosa de tres meses pregunté sobre como podría hacer un test sobre una clase que accediera a la base de datos.


Se formó un buen debate, lo cual me alegra y se agradece. Pero sinceramente, como neófito en esto, me quedé como estaba.

Trabajo con PHP y lo que pretendo "testear" no es si funciona la conexión con la base de datos, ni el funcionamiento del ORM, ni si existen tales datos, etc.
Lo que quiero testear es si un método que escribo, el cual hace un SELECT a la BD, y luego procesa dichos datos, funciona correctamente. No si esto es considerado un test unitario, funcional o de integración. Únicamente pretendo saber como hacer dicho test.

En dicho debate se habló de DB en memoria, doble test, mocks, etc., pero ando mas perdido que un pulpo en un garaje.

Hasta ahora lo que he hecho para testear este tipo de código, es preparar unos fixtures con datos de "prueba", que al inicio del test meto en la base de datos (exclusiva para test) y al finalizar los elimino con TRUNCATE. No se si esto es lo mas recomendable, pero me he encontrado con restricciones de las foreigns keys y en ocasiones me parece "muy tedioso". Por lo que veo en la documentación de PHPUnit y ejemplos, al final siempre se accede a los datos de la BD. En tal caso, ¿para que sirve un mock?.

¿Alguien sabría orientarme sobre esto?.

Saludos cordiales.
Pablo.

Si lo reenvías, ten la precaución de borrar los datos de procedencia que
encabezarían tu reenvío – empezando por mi dirección de correo
electrónico - . Coloca siempre las direcciones de tus contactos en el
campo <CCO> para que viajen discretas, no en el campo <Para> ni en
el<CC>. De esa forma nadie que lo reciba tendrá constancia de las señas
de los demás destinatarios a los que también se remite. Todo ello a fin
de evitar que nadie se aproveche de todas las direcciones que se van
acumulando al pasar de buzón a buzón para el lanzamiento de correo
basura y otras indeseadas lindezas. Aparte claro está de garantizar la
privacidad.

Jesús Jiménez

unread,
Nov 2, 2011, 7:12:56 AM11/2/11
to agile...@googlegroups.com
Hola,

Si no tienes muy claro como funciona el tema mocks, yo te recomendaría no usarlos de momento hasta no entenderlos bien o liarás una en tu código importante (por experiencia...)

Sin usar mocks y por lo tanto accediendo a la BD se considera test de integración (pruebas tu código contra otro sistema externo). Para este test en concreto lo que haces para mi es correcto, creas un juego de datos de prueba en la BD por cada test, haces lo que quieras probar y luego borras una vez hecho el assert que quieres.

Los mock son objetos que simulan ser otros. Es decir, imagínate que tu clase tiene que conectarse a una BD para coger 2 números y luego sumarlos para devolverlos. Si quisieras testear solo la funcionalidad esa de sumar, usarías un mock que simulara ser la BD y te devolviera unos datos concretos (3 y 2 por ejemplo) y luego harías la suma con ellos. Con esto te evitas tener que meter datos en una BD, son más rápidos de ejecutar estos tests, pruebas sólo lo que realmente quieres probar (que suma bien, no que conecta a BD y recoge los valores bien) y puedes lanzar estos tests sin tener ningún tipo de conexión a ninguna BD. Quizás con la BD si es tuya y tu la controlas no se vea tan clara la necesidad de mocks, pero imagínate que tienes que conectarte con un webservice de terceros que la mitad de las veces está caído en desarrollo o va muy lento... pues te creas un mock de este webservice y lanzas tus pruebas contra el mock

Un saludo.


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

Pablo Braulio

unread,
Nov 2, 2011, 7:28:02 AM11/2/11
to agile...@googlegroups.com
Hola Jesús.

Gracias por la aclaración, pero tengo alguna "dudilla".

Si tengo un método metodoX, que hace una consulta a la BD, saca los datos y luego los suma (por ejemplo), al llamar a ese método en el test, este intentará (como es natural) hacer dicho select. ¿Cierto?. O, ¿es que el mock sobreescribe ese método y no hace el select a la BD?.

Saludos cordiales.
Pablo.

Si lo reenvías, ten la precaución de borrar los datos de procedencia que
encabezarían tu reenvío – empezando por mi dirección de correo
electrónico - . Coloca siempre las direcciones de tus contactos en el
campo <CCO> para que viajen discretas, no en el campo <Para> ni en
el<CC>. De esa forma nadie que lo reciba tendrá constancia de las señas
de los demás destinatarios a los que también se remite. Todo ello a fin
de evitar que nadie se aproveche de todas las direcciones que se van
acumulando al pasar de buzón a buzón para el lanzamiento de correo
basura y otras indeseadas lindezas. Aparte claro está de garantizar la
privacidad.



Ismael Ferrer

unread,
Nov 2, 2011, 7:40:48 AM11/2/11
to agile...@googlegroups.com
Ese método tiene demasiada responsabilidad. Deberías refactorizarlo descomponiéndolo en varios métodos de responsabilidad única.

--
Ismael

Jesús Jiménez

unread,
Nov 2, 2011, 7:52:33 AM11/2/11
to agile...@googlegroups.com
(Voy a hablar de lo que conozco, Java, igual hay cosas que no cuadran en PHP, no lo se..)
A ver, para utilizar mocks, lo primero es que tu clase que estás probando no cree el objeto de acceso a BD, sino que sea inyectado (http://es.wikipedia.org/wiki/Inyecci%C3%B3n_de_dependencias), ya que así en vez de pasarle el objeto real de acceso a BD le puedes pasar otro que creas en el test que será tu mock. Te pongo un ejemplo:

Tenemos una clase llamada Sumador que queremos probar y le metemos en el constructor la clase de acceso a BD. Voy a hacer el ejemplo sin usar ningún tipo de framework de mock para que se entienda el problema.

public class Sumador {
      private ClaseAccesoBD bd;

     public Sumador(ClaseAccesoBD bd) {
          this.bd = bd;
     }

     public int sumaNumerosDeLaBD(){
         int numero1 = bd.getNumero1();
         int numero2 = bd.getNumero2();
         return numero1 + numero2;
     }
    
}


Nuestra clase recibe un objeto que será nuestra interfaz con la BD. ClaseAccesoBD será un interfaz:

public interface ClaseAccesoBD {
      public int getNumero1();
      public int getNumero2();
}

Ahora necesitamos una clase que implemente esta interfaz. Habrá una que se conecte a la BD y obtenga esos números, que será tu clase "de producción". Pero vamos a definirnos otra que será nuestro mock.

public class ClaseAccesoBDMock implements ClaseAccesoBD {
     public int getNumero1() {
          return 3;
     }

     public int getNumero2(){
         return 2;
     }
}

Como ves, nuestro mock no hace nada más que devolver unos valores fijos sobre los que luego haremos la suma.

Así nuestro test podría quedar de la siguiente forma:

Sumador sumador = new Sumador(new ClaseAccesoBDMock);
assertThat(sumador.sumaNumerosDeLaBD(), is(5));


Con esto ya hemos probado que nuestro método suma bien.

En el código de producción real habrías puesto new Sumador(new ClaseAccesoDBDeVerdad());

Lo he hecho sencillo, pero podría ser mockear un método que reciba una query y devolviera un listado de números... sería lo mismo. Mi mock pasaría de lo que le llega y devolvería siempre lo mismo. Luego hay frameworks de mocks que te evitan escribir la clase de Mock incluso la necesidad de interfaz (porque en PHP...) Simplemente en esos frameworks defines un objeto mock de la clase de acceso a la BD donde le dices que tus métodos getNumero1 y getNumero2 devuelvan siempre 2 y 3 y ya está. Luego se lo pasarás a tu Sumador y todo lo demás igual.
Como ves, la clase Sumador no cambia, da igual que sea el acceso a BD de verdad o el mockeado.

Echa un vistazo a http://weblogs.javahispano.org/artesanodeprimera/ y seguro que te ayuda a aclarar un poco más el tema mocks.

Pablo Braulio

unread,
Nov 2, 2011, 7:53:49 AM11/2/11
to agile...@googlegroups.com
¿Te refieres ha hacer esto?.

protected function select(){...}
protected function suma($datos){...}
public funcion (getXX){
$val= $this->select; ...
$this->suma($val)
}

Lo he puesto de modo resumido para que se entienda.

Y en tal caso lo que se testea es el método suma, ¿cierto?.
De ser así me parece muy tedioso, ya que para hacer eso (suponiendo que un test se escribe antes que el código), tienes que tener muy muy claro como lo vas a refactorizar antes de nada.

Saludos cordiales.
Pablo.

Si lo reenvías, ten la precaución de borrar los datos de procedencia que
encabezarían tu reenvío – empezando por mi dirección de correo
electrónico - . Coloca siempre las direcciones de tus contactos en el
campo <CCO> para que viajen discretas, no en el campo <Para> ni en
el<CC>. De esa forma nadie que lo reciba tendrá constancia de las señas
de los demás destinatarios a los que también se remite. Todo ello a fin
de evitar que nadie se aproveche de todas las direcciones que se van
acumulando al pasar de buzón a buzón para el lanzamiento de correo
basura y otras indeseadas lindezas. Aparte claro está de garantizar la
privacidad.



Ernesto Arroyo

unread,
Nov 2, 2011, 2:48:35 PM11/2/11
to agile...@googlegroups.com, Lista Agile-spain
DBUnit?

al menos yo lo uso para esto, pero en java... supongo que habrá versión php o algo, pero creo que es justo lo que necesitas

Ernesto Arroyo

... enviado desde mi iPad
--

Pablo Braulio

unread,
Nov 3, 2011, 4:58:50 AM11/3/11
to agile...@googlegroups.com
Bueno, gracias a todos por vuestros comentarios. Voy sacando algo claro.

La conclusión a la que llego es:
-Para testear un método con la BD (que haga un insert, p.e.), puedo usar dbUnit (¿test de integración?). Que phpunit tiene una extensión para ello. http://www.phpunit.de/manual/current/en/database.html
-Para testear un método que recoge valores de la BD y los procesa. Refactorizar el query y de ese modo se puede usar mock contra ese método y el resto un test unitario. Creo que eso es a lo que se refería Ismael.

Muchas gracias.

Saludos cordiales.
Pablo.

Si lo reenvías, ten la precaución de borrar los datos de procedencia que
encabezarían tu reenvío – empezando por mi dirección de correo
electrónico - . Coloca siempre las direcciones de tus contactos en el
campo <CCO> para que viajen discretas, no en el campo <Para> ni en
el<CC>. De esa forma nadie que lo reciba tendrá constancia de las señas
de los demás destinatarios a los que también se remite. Todo ello a fin
de evitar que nadie se aproveche de todas las direcciones que se van
acumulando al pasar de buzón a buzón para el lanzamiento de correo
basura y otras indeseadas lindezas. Aparte claro está de garantizar la
privacidad.



Ismael Ferrer

unread,
Nov 3, 2011, 5:09:11 AM11/3/11
to agile...@googlegroups.com
Efectivamente, me entendiste a pesar de que mi explicación no estaba muy clara.

Separa responsabilidades refactorizando y prueba la responsabilidad concreta que quieres probar de forma aislada sustituyendo la que no quieres probar (el acceso a base de datos por ejemplo) por un mock mediante inyección de dependencias.

Un saludo.

--
Ismael

Pablo Braulio

unread,
Nov 3, 2011, 5:12:55 AM11/3/11
to agile...@googlegroups.com
Ok, pues lo dicho. Muchas gracias.

Saludos cordiales.
Pablo.

Si lo reenvías, ten la precaución de borrar los datos de procedencia que
encabezarían tu reenvío – empezando por mi dirección de correo
electrónico - . Coloca siempre las direcciones de tus contactos en el
campo <CCO> para que viajen discretas, no en el campo <Para> ni en
el<CC>. De esa forma nadie que lo reciba tendrá constancia de las señas
de los demás destinatarios a los que también se remite. Todo ello a fin
de evitar que nadie se aproveche de todas las direcciones que se van
acumulando al pasar de buzón a buzón para el lanzamiento de correo
basura y otras indeseadas lindezas. Aparte claro está de garantizar la
privacidad.



Jesús Jiménez

unread,
Nov 3, 2011, 5:17:07 AM11/3/11
to agile...@googlegroups.com
Te recomiendo que le eches un vistazo a los videos de Sebastian Hermida sobre TDD: http://www.holatdd.com/
En ellos usa mocks y quizás te quede más claro :)


Ernesto Arroyo

unread,
Nov 3, 2011, 6:52:37 AM11/3/11
to agile...@googlegroups.com, agile...@googlegroups.com
basicamente, al menos en mis diseños, el servicio accede a la base de datos a través de un DAO. En las pruebas usamos un DAO falso (llámalo mock) que no conecta con la bbdd real sino con la que le damos con dbunit que para el servicio es lo mismo pero yo la recreo en cada test unitario con el dbunit.

a riesgo de ser pedante:

tengo un ejemplito


Ernesto Arroyo

... enviado desde mi iPad
Reply all
Reply to author
Forward
0 new messages