Flow expectation test con Rhino Mocks – Parte 1

En varias de mis entradas sobre unit testing hablo sobre la potencia de esta librería de rhino mocks para hacer unit test, como en ésta sobre herramientas para unit test. Pero no solo sobre unit test debe sustentarse tu aplicación.

Hay otro tipo de test para probar el flujo de tu lógica de negocio, que son los culpables de que tome mi decisión sobre usar esta librería rhino mocks sobre otras librerías de testing. Sobre este tipo de test hablo en una entrada reciente, dando motivos para implementar este tipo de test y una arquitectura que los permita.

En esta entrada voy a entrar al detalle de como usar rhino mock para hacer diferentes flow expectation test. 

Para hacer un flow expectation test es recomendable, en primer lugar, que tu System Under Test (SUT), solo tenga llamadas a otros métodos, llamadas que puedas verificar que se realizan. Para ello necesitas que sean llamadas públicas a otras interfaces. Digo interfaces y no clases ya que es necesario que sean interfaces para que puedas crear mock sobre ellas de forma dinámica y rápida (con una línea de código) gracias a rhino mock.

Además las interfaces sobre las que haces estás llamadas, deben formar parte del constructor de tu clase SUT, para que puedas indicar que tu clase va a funcionar con los mocks de esas interfaces, y no con sus implementaciones reales de cara a los test. Pero a su vez, esa clase debe funcionar con implementaciones reales en tu aplicación, resolviendo las interfaces mediante DI (Dependency injection) mediante Unity, Autofac o librerías similares.

Para ello, te presento este ejemplo sobre una clase sobre la que vamos a aplicar este tipo de flow expetation test.

Como puedes ver tenemos diferentes llamadas a métodos de una interfaz llamada IRoleDomainService (linea 4), que a su vez se recibe en el constructor de tu SUT (linea 8), que es RoleBusinessService.

En concreto tu SUT es el método UpdateRolesToUserIntoBusiness, que actualizará los roles para un usuario dentro de un determinado business, y este test estará desacoplado de todos los métodos de roleDomainService, de los que solo queremos verificar que se producen las llamadas.

En primer lugar deberás definir un mock de esta interfaz, esto deberás hacerlo al comienzo de la ejecución de tu clase de test, lo cual, usando la librería NUnit, se representa con un método decorado con SetUp.

Ya has mockeado la interfaz, con la linea MockRepository.GenerateStrictMock<IRoleDomainService>(). Este tipo de mock “GenerateStrictMock” son útiles cuando deseas validar el orden en que se hacen las llamadas a métodos de ese mock, si solo deseas stubs que simulen una respuesta ante una llamada a sus métodos, bastaría con “GenerateMock”. Hay otros mocks como “GenerateDynamicMock”, pero sobre esto escribiré en otras entradas.

Ahora necesitas indicar que tu clase SUT va a usar este mock, y no una implementación real.

Para usar el mock debes añadir la declaración de la variable de tu SUT, y la instancias con una instanciación de su implementación real, no con un mock, ya que tu sistema bajo test debe ser el real, pero observa como en su primer parámetro, hemos añadido el mock que hemos generado previamente. Ahora cada vez que tu clase vaya a usar métodos de esta interfaz, será necesario crear un stub, de lo contrario tu test no funcionará ya que no sabrá que debe hacer con los métodos del mock, que devuelven o que parámetros deben usar.

Hay diferentes tipos de stub, para pruebas de flujo los que debes usar son “.Expect( )”. En seguida te presento un ejemplo, antes debes saber que si quieres probar el orden, debes usar el strictMock que has generado, de forma ordenada. Para ello, añade el siguiente código en tu método de test, que también añado en el siguiente ejemplo.

Ya tenemos un Test, y en la línea 11 un using de tu roleDomainService.GetMockRepository().Ordered(), que permitirá hacer test de flujo de llamadas.

Ahora debes identificar todas las llamadas que haces, con un stub de cada uno de los métodos llamados, de tipo “Expect( )”.

Como puedes ver la sintaxis es bien sencilla:

  • claseMock.Expect(x => x.metodoStub(Arg<tipoArgumento>.Is.Anything, …).Return(objeto devuelto)

La notación Arg<type>.Is.Anything es usada en los Stubs por rhino mock para indicar que no importa cual sea el parametro de entrada, el resultado de la llamada a tu método siempre será el mismo return. En este tipo de test de flujo de llamadas precisamente nos dan igual los inputs, solo queremos outputs a cada llamada que garanticen que el flujo de llamadas puede continuar. Para ello debes garantizar que el objeto devuelto tenga al menos las propiedades necesarias para las siguientes llamadas como inputs. Da igual cuales sean sus valores, pero al menos deben estar definidos.

Por este motivo, para que tu flujo de llamadas avance, es importante que los sitemas a los que apliques este tipo de test, procuren siempre tener solo llamadas, sin lógica adicional para no complicar el test, no es que sea un impedimento, pero puede ser una dificultad y una mala práctica desde el punto de vista de la testeabilidad del código.

Pues bien, ahora tu test ya tiene todas las condiciones que necesita para poder pensar en ejecutarlo. ¿Qué necesitas para ejecutarlo? Hacer la llamada al método que pretendes probar.

En la línea 22 he situado la llamada, como puedes ver el parámetro que usa como entrada es solo una instanciación sin ningún valor en sus propiedades, ya que en el caso de los inputs de nuestros métodos stub, esto da igual. Si consultas el código de ejemplo que presento al comienzo de la entrada, lo único que necesitas es que el objeto de entrada no sea nulo para que nuestra lógica pueda avanzar.

Solo te falta una cosa para terminar tu test, los assert de las condiciones de salida para comprobar que el resultado de la ejecución de tu test es el esperado. En este caso, los test de flow expectation calls no usan assert, la librería de rhino mock puede hacer estas verificaciones y hacer que tu test pase o no, a través de un método de tu strictMock llamado VerifyAllExpectations.

Pues ya está listo. Si alguna de esas llamadas es cambiada de orden, eliminada, o se insertan nuevas llamadas en tu lógica, este test se romperá, devolviendo rojo y debiendo ser refactorizado para ajustarse a la nueva situación de tu lógica de negocio.

Te estarás preguntando que sucede cuando tienes diferentes interfaces llamadas desde tu método SUT, pues en este caso necesitarás diferentes stricMock y por tanto diferentes usings como el de la línea 11. ¿Cómo representas que una llamada debe ir detrás o delante de otra si estás en diferentes usings de mocks.ordered?

Es una buena pregunta, que añade complejidad al test, por ello recomiendo una arquitectura como la que sigo aquí, donde una capa de servicio solo llama a una capa de dominio intermedia que se encarga de gestionar las llamadas a todas esas diferentes interfaces, de forma que el test se mantenga con esta limpieza, sobre ello hablo en esta entrada sobre flow expectation tst como parte de tus unit test.

No obstante hacerlo no es imposible, ni excesivamente dificil, pero añade complejidad adicional a tus test que hace que sean un puntito mas difíciles de crear, leer o modificar. La respuesta a esta pregunta la puedes encontrar en esta otra entrada sobre flow expectation test con diferentes interfaces.

Confío en que esta explicación te será de utilidad y te motive a replantearte cómo haces tus desarrollos, orientándote hacia una arquitectura más limpia, sostenible y testeable.

Un saludo, nos vemos en otra entrada para continuar ampliando la skill sobre unit test de back end en .Net

Deja un comentario