jueves, 18 de julio de 2013

Mocking de servicios REST

No se si a vosotros os pasará como a mi , pero cada vez más tiendo a dejar los frameworks web JAVA como Struts, Spring MVC, JSF, GWT, etc a un lado y uso más HTML5, servicios REST y Frameworks MVC Javascript.

Desde que descubrí este tipo de frameworks  MVC Javascript cada vez los uso más, Os recomiendo que le echéis una ojeada a AngularJS,  Backbone o a mi favorito EmberJS.

Las aplicaciones desarrolladas con estos frameworks acaban siendo normalmente aplicaciones web "monopágina" donde la mayor parte de trabajo de ejecución no se lleva a cabo en el servidor sino en el navegador del usuario de la aplicación (por fin un poco de justicia! :) ).   Esto no quiere decir que el servidor solo se encargue de servir páginas y recursos estáticos: aquellas funciones que no pueden implementarse en Javascript en el cliente se pueden implementar en el servidor con la ayuda de las herramientas de nuestra elección y con servicios REST y peticiones y respuestas en formato JSON.

El ciclo de desarrollo de una aplicación web con HTML, CSS y Javascript es muy ligero, uno modifica el código de estos elementos refresca el navegador y puede comprobar los resultados.  Con los componentes del lado de servidor desarrollados en Java la cosa cambia...;  hay que compilar, empaquetar y desplegar.  Algunos sabréis que existen herramientas que alivian este ciclo tan pesado, yo soy muy fan por ejemplo del servidor web Jetty el cual lo puedes usar como un plugin desde Maven y lo ejecutas en el mismo espacio de trabajo de tu aplicación, con lo que al menos las tareas de empaquetamiento y despliegue te las puedes ahorrar, pero aun y así puede resultar demasiado pesado y además Jetty está limitado en cuanto a prestaciones se refiere.

Mocks

Lo que explico aquí solo vale para una aplicación web desarrollada con HTML, CSS  y Javascript : nada de JSPs, ni taglibs, ni scripts de servidor solo AJAX (con JQuery por ejemplo).  En este escenario de lo único que depende esa aplicación es que los servicios REST que usa respondan correctamente a las peticiones AJAX.   ¿Y si pudiéramos simular los servicios REST?  Si pudieramos generar dobles de los servicios originales de los servicios REST con la misma "firma", o sea que respondieran a las peticiones Ajax de la misma manera en que lo hacen los originales, entonces podríamos desarrollar y probar la capa de presentación de nuestra aplicación web sin necesidad del servidor de aplicaciones.  Dicho de otra manera:  Los componentes de servidor  (desde los servicios REST hacia abajo) y la capa de presentación (la aplicación Javascript) se podrían desarrollar en paralelo.

Wiremock

Wiremock es un librería para crear dobles de pruebas de servicios web, funciona creando un servidor web (Jetty casualmente) mediante el que se pueden servir los recursos estáticos de la aplicación y ejecutar los dobles de pruebas de los servicios web.

Para hacerlo bien los dobles de pruebas deben estar en la misma dirección / url que los originales, deben aceptar el mismo tipo de peticiones, con el mismo tipo de cabeceras y métodos http (GET; POST, PUT, ...), deben devolver datos con el mismo formato y los mismos códigos de respuesta HTTP.

Así pues imaginemos que tenemos un servicio REST que devuelve la información de un usuario en JSON,  la url de dicho servicio es rest/user/{login_usuario} y atiende a peticiones mediante el método GET.  El JSON devuelto por el servicio podría tener este aspecto:



Para simular com wiremock un doble para este servicio hay que hacer dos cosas:

  1.  Crear un fichero JSON con ese contenido u otro similar.
  2. Crear un mapeo para dicho servicio, un ejemplo de esto sería crear un fichero como este:

{
    "name": "Julian Rodríguez",
    "role": "OPERATOR",
    "userName": "jrodriguez",
    "organizationName": "Rodriguez industries s.l.",
    "organizationId" : 2
}

Wiremock está pensado para ser usado con frameworks de pruebas como JUnit, sin embargo en esta entrada os explico como lo he integrado yo en mi entorno de trabajo para ejecutar mi aplicación web y probarla si necesidad de los servicios REST reales.

Integración en un proyecto Maven

No os voy a marear con todos los detalles acerca del proyecto que estoy desarrollando, lo único que importa es que estoy desarrollando una aplicación web java con Maven como herramienta de construcción.
Eso significa que mi proyecto tiene una estructura de directorios similar a esta:



Todos los que useis Maven ya debéis conocer esta estructura:  Para poder usar wiremock tuve que incluir la siguiente dependencia en mi proyecto:



<dependency>
          <groupId>com.github.tomakehurst</groupId>
          <artifactId>wiremock</artifactId>
          <version>1.32</version>
         <classifier>standalone</classifier>
          <scope>test</scope>
</dependency>

Puse el scope test en tanto que la librería solo la uso con la finalidad de hacer pruebas y no quiero tenerla incluida en el war final resultante del proceso de construcción.

Para configurar wiremock son necesarios pocos parámetros:  Un número de puerto http donde se ejecutará el servidor web y un path para especificar el directorio raíz.  Dicho directorio raiz debe contener dos directorios:  uno llamado __files/ que debe contener los recursos estáticos de la aplicación y otro llamado mappings/ que contiene ficheros de mapeo como el que os he descrito en el apartado anterior.  Para los que conocéis bien maven seguramente seguramente os debe chocar eso del directorio __files, pues bien si queremos usar wiremock hay que cambiar el nombre del directorio webapp/ por __files/ ;  si después de leer esto alguien encuentra la manera de hacer lo mismo sin tener que cambiar el nombre de este directorio comentadlo.  Así pues mi estructura de directorios queda de la siguiente manera:



Para que todo funcione bien  hay que cambiar la configuración del plugin war para que coja los recursos del directorio que corresponde y no incluya en el war los ficheros relacionados con los mocks:


<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-war-plugin</artifactId>
    <version>2.3</version>
    <configuration>
 <warSourceDirectory>src/main/__files/</warSourceDirectory>
 <excludes>mocks/**</excludes>
    </configuration>
 </plugin>

Para iniciar wiremock sobre los ficheros de nuestro proyecto creé una clase que incluí dentro los tests (en src/test/java) , el código de esta clase:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
public class MockServerRunner {

    public static void main(String[] args) {        
        WireMockConfiguration wireMockConfiguration = WireMockConfiguration.wireMockConfig();
        wireMockConfiguration.withRootDirectory("src/main");
        wireMockConfiguration.port(8082);
        Log4jConfiguration.configureLogging(true);

        WireMockServer wireMockServer = new WireMockServer(wireMockConfiguration);

        wireMockServer.start();
    }

}

En esa clase he destacado los dos parámetros que os comentaba antes, el puerto http y el directorio raiz:  como src/main contiene __files y mappings wiremock debe funcionar sin problemas.

El último ingrediente de la mezcla es la configuración del plugin exec-maven-plugin para que ejecutar nuestro servidor wiremock sea más fácil:


<plugin>
              <groupId>org.codehaus.mojo</groupId>
              <artifactId>exec-maven-plugin</artifactId>
              <version>1.2.1</version>
              <configuration>
                    <mainClass>MockServerRunner</mainClass>
                    <classpathScope>test</classpathScope>
              </configuration>
</plugin>

Con esto ejecutar nuestra aplicación web en modo "desarrollo" con la capa de presentación y los mocks es tan fácil como ejecutar en la carpeta del proyecto mvn exec:java  .

Usando esta configuración es posible ejecutar nuestra aplicación web y simular de una forma fácil todo el tráfico de datos de los servicios REST lo cual nos puede ayudar en los siguientes casos:

  • Simular servicios REST de terceros y ver como se comporta nuestra aplicación.
  • Crear prototipos de nuestros servicios REST antes de implementarlos.
  • Probar casos de error excepcionales al ejecutar servicios REST.
  • y como no, ejecutar y modificar nuestra aplicación Javascript sin necesidad de despelgarla.



No hay comentarios: