MirageJS: tu aliado para simular las respuestas del API

, ,

MirageJS es una librería Javascript de ‘mockeo’ de peticiones HTTP para las aplicaciones. ¿Esto qué quiere decir? Esto significa que permite capturar las peticiones HTTP que haga una aplicación y definir la respuesta que quieres que devuelva cada una de ellas, en lugar de la respuesta original.

¿Para qué lo usamos en ALTEN?

Durante el desarrollo de nuestras soluciones front-end en ALTEN hay ocasiones en las que el API a la que debemos atacar para desarrollar la funcionalidad aún no está disponible, pero sí está definido.

En estos casos tendríamos varios caminos de actuación:

  1. Nos esperamos a que se desarrolle el servicio API.
  2. Pedimos al equipo de backend que nos creen y nos expongan unos servicios “de mentira”, con datos estáticos, etc.
  3. Usamos nosotros mismos una librería de mock como MirageJS para resolverlo.

¿Por qué MirageJS?

Una de las principales ventajas de MirageJS es precisamente la que hemos comentado más arriba. Concretamente, permite avanzar en el desarrollo de una aplicación front-end sin necesidad de depender de que el API remoto esté disponible.

Con él podemos olvidarnos de montar un Eclipse o un IntelliJ en la máquina para levantar el proyecto SpringBoot con el API (con todo lo que ello puede conllevar en proyectos realmente complejos). También se deja de depender de un despliegue de la aplicación backend en un entorno de desarrollo que puede estar caído durante un tiempo indeterminado “because of reasons”, entre otros motivos.

Pero las ventajas de MirageJS no acaban ahí, ya que el código que se escriba con MirageJS para ‘mockear’ el API al que se quiera atacar se tendrá en la propia aplicación front. El usuario puede controlarlo el mismo y , además, podrá decidir cuándo y cómo debe aplicarse. Gracias ello no se depende en absoluto de nada externo.

No obstante, existen otras soluciones como Json-server, pero estas implican tener que levantar un servidor aparte, seguir sus convenciones para obtener los datos de los servicios, etc.

Con MirageJS el control lo tiene el usuario por completo, lo cual lo convierte en una solución mucho más potente que las demás.

¿Cómo se usa?

Nuestros desarrollos front-end se realizan con Angular, así que todos los ejemplos irán orientados en ese camino, aunque se puede usar MirageJS con el framework que se quiera.

Lo primero es instalar la dependencia, por supuesto. Para ello no hay más que usar NPM o yarn, como siempre:

npm install --save-dev miragejs

Una vez hecho esto, ya se puede empezar a usar MirageJS creando una instancia de su objeto Server. En nuestro caso, al ser aplicaciones Angular, el mejor punto para crearla es el fichero app.module.ts:

// …
import { Server } from 'miragejs';

new Server({
   routes() {
     this.namespace = '/api';

     this.get('/healthcheck', (schema, request) => {
       return {
         status: 'ok'
       };
     });
   }
 });


@NgModule({
  // …
})
export class AppModule {
  // …
}

Aquí se puede ver uno de los conceptos básicos de MirageJS: las routes. En este instante es donde se define todos y cada uno de los endpoints que se quiere ‘mockear’.

En este caso estamos capturando todas las peticiones GET que nuestra aplicación haga a la URL /api/healthcheck y, como respuesta, devolvemos un JSON estático. Esto abre un mundo de posibilidades porque ya podemos simular todos los endpoints de los que dependamos para desarrollar la app.

Pero esto no es todo… Introducing the ORM!

MirageJS incluye una base de datos en memoria y un ORM con el que gestionarla. De esta forma puedes simular aún con más detalle el comportamiento de los servicios.

¿Qué quiero decir con esto? Pues bien: imagina que tienes mockeado un servicio que devuelve el listado de Pokemon que tiene un usuario (GET /api/pokemon). Y que tienes otro servicio para añadir Pokemon a la colección de dicho usuario (POST /api/pokemon).

Gracias a la base de datos y al ORM de MirageJS podrías hacer que el servicio de añadir pokemon (POST /api/pokemon) guardase realmente el Pokemon que recibe, para que el servicio de listado (GET /api/pokemon) lo devolviese en futuras llamadas.

Veamos este ejemplo en código:

new Server({
   models: {
     pokemon: Model,
   },

   routes() {
     this.namespace = '/api';

     this.get('/pokemon', (schema, request) => {
       return schema.pokemons.all();
     });

     this.post('/pokemon', (schema, request) => {
       const attrs = JSON.parse(request.requestBody);

       return schema.pokemons.create(attrs);
     });
   },
 });

Aquí hay varias cosas en las que fijarse:

  • Cómo definimos el Model “pokemon” dentro de la sección de models del Server
  • Cómo usamos el ORM a través del objeto schema
  • Cómo nuestro modelo pokemon se pluraliza en el ORM para convertirse en pokemons a la hora de utilizarlo para acceder a la colección
  • Cómo usamos los métodos .all() y .create() del ORM para trabajar con la colección

Y todo esto no es más que la punta del iceberg, el ORM te permite definir relaciones entre modelos (tipo hasMany o belongsTo) y utilizar dicha información para hacer búsquedas y demás operaciones, exactamente igual que con cualquier otro ORM.

La guinda del pastel: Seeds y Factories

Como colofón, y para ponerte las cosas aún más fáciles, MirageJS provee un sistema super sencillo para realizar una precarga de datos en el sistema.

Siguiendo con el ejemplo anterior, imaginad que necesitamos dar de alta en el sistema una serie de Pokemon, que son los que el usuario ya tiene en su colección. Para hacerlo podemos usar los seeds:

new Server({
   models: {
     pokemon: Model,
   },

   seeds(server) {
     server.create('pokemon', {
       name: 'Charmander'
     });

     server.create('pokemon', {
       name: 'Bulbasaur'
     });
   },
 });

Como veis, con el método create() del objeto server hemos podido crear datos iniciales para nuestra colección de pokemon. Pero dar de alta elementos uno a uno es un poco tedioso. ¿Qué ocurre si queremos dar de alta 10000 elementos para comprobar cómo se comporta nuestra UI? ¡createList() y las Factories al rescate!

Con las Factories puedes definir con qué atributos se crearán tus objetos automáticamente (aquellos objetos creados desde seeds, por supuesto), de manera programática. Lo vemos con un ejemplo:

new Server({
   models: {
     pokemon: Model,
   },

   factories: {
     pokemon: Factory.extend({
       name: (i) => `Example pokemon #`
     })
   },

   seeds(server) {
     server.createList('pokemon', 10000);
   },
 });

Bonus track: e2e testing

Si ya os he convencido de que MirageJS es una librería fantástica para poder desarrollar las apps sin necesidad de depender de un backend, pensad ahora las posibilidades que da en cuanto al testing e2e.

Se puede usar MirageJS para definir el estado de los datos en cada punto de la ejecución de los tests, con lo que se tiene control total de las pruebas. Ya no se depende de tener un entorno de QA independiente al que haya que ‘resetearle’ los datos cada vez que se necesite lanzar los tests.

Además, hay que pensar también en lo rápido que irán dichos tests sin depender del tiempo de respuesta del backend. ¿Suena bien, verdad?

¡Esperamos que os haya gustado nuestro nuevo post! ¡Volvemos la semana que viene con más!

Web Team
ALTEN