Buscar en este blog....

jueves, 25 de diciembre de 2014

Fakes, Stubs y Mocks... ¿cuál es la diferencia?

Buenas!
Como me viene pasando últimamente, pasan décadas de un posteo a otro. Sin embargo y pese a ello: sigo posteando ;)

El tema de este post surgió nada menos que de darme cuenta que los conceptos que voy a explicar, no estaban adecuadamente explicados en la facultad a la que asistí. Quizás esto ya haya cambiado, pero considero que son términos importantes como para estar mezclándolos cual ensalada de frutas.

Básicamente, donde yo estudié la idea -muy genérica y nada desarrollada- de un Stub nunca se mencionó la diferencia entre Stubs y Mocks, y tampoco la diferencia entre éstos dos con un Fake. Es más, me atrevo a decir que nunca se habló de Fakes y Mocks. Pero bueno, no es el fin de este artículo evaluar ni criticar lo que tuve que estudiar, sino más bien, aclarar los conceptos.

Todo esto viene a colación de la importancia que han tomado los Unit Tests (Tests de Unidad) de los cuales no voy a hablar ahora para no extenderme en el tema. Doy por hecho que ya sabes lo que son.

Pero, ¿por qué es importante? 

Es de suprema importancia realizar UTs que sean legibles, que cualquier persona que nunca los vio antes pueda leerlos y darse cuenta de inmediato qué quiso testear quien lo escribió. Si esto no es así, se pierde la filosofía que está detrás de los UT, que es justamente proporcionar un método muy rápido de testeo. Si un programador tiene que dedicar mucho tiempo a entender un test que ha dado FAIL (o peor aún: muchos tests!) por el solo hecho de que se escribió mal o que es ilegible, el resultado es terriblemente predecible: no lo va a leer y lo va a dejar de lado porque entender y/o arreglar ese test le va a consumir mucho tiempo, y seguramente "tiene cosas más importantes que hacer". Se ignora el test, se lo marca como obsoleto, etc. Y allí comienza la decadencia del sistema. Y de la calidad. Y de la mantenibilidad. Y etc, etc.

Siempre es una necesidad que tengas claro los conceptos referidos a tu especialidad, a lo que te dedicas a hacer.

Antes que nada, esta entrada presupone que ya sabes lo que es un test de unidad, cual es su finalidad, y que significan las tres A (Arrange-Act-Assert). Si no es asi, sería bueno que le googlearas por ahí.

Bien, comencemos entonces:

¿Qué es un Fake?
El verbo "to fake" en inglés es fingir, falsificar. Eso es lo que es un Fake: una falsificación de un objeto. (Hablo de "objetos" por que normalmente en mi blog me refiero a la OO (orientación a objetos) por ser la que más utilizo, pero es aplicable también a otros paradigmas.)

Es decir, es un objeto que es una falsificación de otro. Se ve "igual" pero se comporta distinto.

¿Y para qué sirve?
Porque si queremos realizar pruebas de unidad, es indispensable que estas sean rápidas! Pero no sólo por esto! Sobre todo que el comportamiento de la clase (o unidad de trabajo) que estamos testeando (llamémsole UUT de "Unit Under Test") no se debe ver influenciado por el comportamiento de algún otro objeto del que pueda hacer uso. Si por ejemplo queremos testear el método de una clase que por dentro hace uso de un objeto que se comunica con la base de datos, tenemos dos problemas:

  1. El objeto que utiliza la base de datos (llamemosle "ConexionConBD") hará muy lento el testing, ya que debe conectarse con la mismísima base de datos, entorpeciendo este principio.
  2. Si el objeto "ConexionConBD" falla cuando nosotros esperamos que no falle, entonces fallará también el UUT (nuestra unidad bajo testing)..
  3. Si la mismísima base de datos falla (por la razón que fuere, cualquiera), nuevamente fallará también nuestro UUT.

Y el problema con las fallas que son "ajenas" a nuestro UUT, es que finalmente no vamos a saber si lo que falla es el UUT, o es alguno de los objetos que éste utiliza. Y pierde sentido el concepto de "Test de UNIDAD".

Entonces, para solucionarlo, inventemos un "ConexionConBD" que responda lo que nosotros queramos, cuando nosotros queramos, y como nosotros queramos. Es decir, falsifiquemos esa clase. Para ello, crearemos un Fake de la clase "ConexionConBD", la cual podemos controlar desde el test que estamos escribiendo.

No voy a entrar en detalles técnicos de como se hace, ya mucho en internet sobre como hacerlo.

Entonces....

¿Qué es un Stub?
El Stub, es ni más ni menos, un subtipo de Fake. Si, asi de simple: los Fakes se dividen en dos clases: o son Stubs, o son Mocks. ¿Y de qué depende? De según como se use, de eso depende!

En el caso anterior, podemos utilizar el Fake "ConexionConBD" para que siempre nos devuelva un valor esperado, así no nos encontramos con sorpresas. Una vez que el fake nos devuelve el valor que esperamos, podemos proseguir con el test.

Pero al usar este fake como un Stub, éste sólo puede utilizarse para devolver un valor o arrojar una exepción. Pero nada más. NADA MÁS! Es decir, el test se sigue haciendo directamente contra nuestro UUT.



¿Y qué es un Mock?
En el caso anterior, el fake se utiliza solo para que devuelva un valor esperado, es decir, que realice un comportamiento definido y así poder proseguir con nuestro test.

Pero en otras ocasiones, el test mismo consiste en saber si el UUT se comunicó de forma esperada con otro objeto. Por ejemplo, queremos saber si el UUT llamó al objeto "ConexionConDB" de forma correcta. En este caso, no podemos hacer un assert directamente contra la UUT, ya que la UUT mismo no sabe si la llamada -a un método por ejemplo- fue realizada con éxito. Quién sabe esto, es el objeto llamado en cuestión, es decir, el objeto "ConexionConBD". Pero el "ConexionConBD" original  (aquel que se conecta con la base de datos) no está programado para decirnos si la llamada que le hicieron fue correcta, y encima, se conecta con la base de datos! (algo que queremos evitar!).

Solución: le hacemos un fake. Pero este fake no devuelve un valor, sino que simplemente recibe la llmada que le hace la UUT y, nuestro test, en lugar de hacer un assert contra la UUT, lo hace contra el Fake.



Y esta es la clave: cuándo nuestro test verifica (assert) contra un fake, este fake es un Mock.
Si en cambio la verificación se hace contra la UUT, entonces cualquier fake que haya será un Stub.

Resumiendo.

Todo fake será indefectiblemente un Stub o un Mock dependiendo de cómo se lo utilice. Es más, un fake puede ser Stub en un test, y Mock en otro test. La diferencia está en el uso que se le da a ese fake. Nada más y nada menos.

Según Roy Osherove (The Art Of Unit Testing, un libro que recomiendo totalmente y del cual saqué la idea para las imágenes) cada UT (Unit Test) no debería tener más de un Mock, y sí puede tener más de un Stub. Osherove es partidario de que si un UT tiene más de un Mock, el test pierde legibilidad y se hace más complicado de entender "inmediatamente" qué es lo que se quiere testear.

Espero que haya sido suficientemente claro, ya que es un tema de muchas confusiones por lo que he visto yo.

Hasta pronto, espero!

1 comentario:

Comments are subject to moderation, only in order to avoid insults and disguising things.

Los comentarios están sujetos a moderación, solo con el fin de evitar insultos y cosas por el estilo.