Flow expectation test con Rhino Mocks – Parte 2

En la primera entrada sobre Rhino Mocks presento una primera aproximación a los flow expectation test. En esa entrada hablo sobre validar que todas las llamadas a otros métodos son realizadas por el método que deseas probar. En ese caso se esta utilizando una misma interfaz, por lo tanto los flow expectation test son bastante sencillos. Esta situación no siempre se va a dar, tendrás muchos casos donde tus servicios llamen a métodos de distintas interfaces, y te será necesario combinar diferentes mocks para hacer este tipo de test, en esta entrada te presento como hacerlo con Rhino Mocks.

Voy a partir del mismo ejemplo que en la parte 1 de esta entrada sobre flow expectation test con Rhino Mocks. La diferencia es que mientras que en la anterior entrada solo se utilizaba una interfaz IRoleDomainService, ahora se utilizarán diferentes interfaces que harán que el test sea más complejo.

En este caso he sustituido algunas de las llamadas de métodos de IRoleDomainService por interfaces como IUserDomainService, IRoleValidationService o IRoleRepository. Estoy improvisando para modificar el ejemplo de forma que necesitemos hasta 4 mocks de estas interfaces y el orden de las llamadas se alterne entre estas interfaces, para buscar el efecto que persigo enseñarte de mayor complejidad.

Como puedes ver en el ejemplo anterior, tenemos diferentes situaciones:

  • Interfaces con una única llamada: GetUser de IUserDomainService y ValidationBusinessUserRole de IRoleValidationService.
  • Interfaces con múltiples llamadas no consecutivas: GetUserRoles y RolesCanBeUpdatedByUser de IRoleDomainService.
  • Interfaces con múltiples llamadas consecutivas: UpdateRoles y SaveContext de IRoleRepository.

Siguiendo el ejemplo que presento en la parte 1 de esta entrada, voy a crear en primer lugar el test con todos los mocks en la inicialización de los test y también crearé la instanciación de la clase RoleBusinessService con todos estos mocks.

Ya está todo listo, ahora voy a preparar el test, con cada uno de los usings de estos mocks.GetMockRepository.Ordered que voy a necesitar, también la llamada al sistema sometido a test, nuestro método del servicio del que parte este ejemplo, y además todas las verificaciones de estos mocks.Ordered, aunque no los voy a construir con el fin de explicarlo paso a paso a continuación.

Ahora lo más sencillo será introducir cada una de las llamadas en nuestro test, justo en el orden en que son llamadas, y cuando se trate de interfaces distintas, podrás usar los siguientes métodos de Rhino.Mocks.

  • WhenCalled: Realizará alguna de las dos verificaciones siguientes siempre que este método sea llamado.
  • AssertWasCalled: verificará si un método ha sido llamado antes de la llamada actual.
  • AssertWasNotCalled: verificará que un método no haya sido llamado antes de la llamada actual.

Gracias a estos tres métodos, podrás organizar el orden entre métodos que pertenecen a diferentes interfaces, en diferentes usings de mocks.Ordered(). Comencemos.

El primer método es userDomainService.GetUser, dado que el siguiente método pertenece a otra interfaz (roleDomainService.GetUserRoles), deberás usar la siguiente sintaxis para validar que este método aún no ha sido llamado.

El siguiente método roleDomainService.GetUserRoles, no comparte interfaz con el método anterior ni el siguiente, por lo tanto deberás usar tanto AssertWasCalled como AssertWasNotCalled.

El tercer caso es similar a este último, en cambio, el 4º caso, repite interfaz, IRoleDomainService, por lo tanto irá en el mismo using que este código anterior, pero dado que las llamadas no son consecutivas, ambos métodos de IRoleDomainService, GetUserRoles y RolesCanBeUpdatedByUser deberán tener tanto AssertWasCalled de su método inmediatamente anterior como AssertWasNotCalled de su método inmediatamente siguiente, como se acaba de hacer para GetUserRoles.

Una situación distinta se da para los dos últimos métodos llamados, en este caso IRoleRepository tiene 2 llamadas consecutivas, y dado que están en el mismo using de mock.Ordered(), en primero de ellos solo necesita el AssertWasCalled del método anterior, pero no el AssertWasNotCalled del método siguiente, ya que mocks.Ordered() puede verificar este orden consecutivo por si solo, como sucede con la parte 1 de esta entrada sobre flow expectation test con Rhino Mocks.

De igual forma, la segunda llamada de IRoleRepository no necesita un AssertWasNotCalled, ya que es la última llamada, no hay ninguna otra detrás, y tampoco necesita el AssertWasCalled, ya que la llamada inmediatamente anterior pertece al mismo using de mock.Ordered().

Con estas premisas, el test queda de la siguiente manera:

Si ahora comparas este ejemplo, con el que presento en la parte 1 de esta entrada al final de la misma, donde ya presento el test terminado, podrás ver que en este caso, el test acabado tiene un aspecto mucho menos limpio, mas complejo, mucho mas difícil de seguir en la lectura, de mantener, y muy poco transparente, pese a que su funcionamiento es exactamente el mismo.

Por ello, para conseguir test de flow expectation como el de la parte 1, recomiendo una capa intermedia en tu arquitectura, que posibilite que tengas una interfaz que conecte tu capa de servicio con tus interfaces de capa de dominio permitiendo a tu servicio llamar a una única interfaz que haga de enganche o proxy entre estas capas, de forma que estos test serán infinitamente más fáciles. Sobre este aspecto de arquitectura hablo en mi entrada sobre cómo añadir flow expectation test a tu batería de unit test.

Ahora juzga por ti mismo, viendo los test de la parte 1 y de la parte 2, junto con la explicación de por qué los test de flow expectation son importantes, si te merece la pena aplicar o no la arquitectura que te propongo.

Un saludo.

Deja un comentario