En un mundo ideal a los frontends nos gustaría que el equipo de backend vayan desarrollando por delante nuestra para tener listas las APIs que necesitamos en nuestro desarrollo cuando lleguemos a ello, pero como sabemos, esto no siempre es posible y por ello hoy os voy a hablar del Mock Service Worker (MSW) y de cómo nos facilita enormemente el desarrollo.
También vamos a ver los Object Mother, cómo los podemos usar en nuestro MSW y cómo generar datos aleatorios para hacer más real nuestras APIs.
Todo esto lo voy a explicar orientado a Angular con un pequeño proyecto que he desarrollado y al que vais a tener acceso.
Casi todo lo que voy a explicar es aplicable a otros frameworks, pero por si te quedan dudas, te voy a dejar el enlace a ejemplos oficiales de los creadores de la librería mswjs y el enlace a la documentación oficial.
Índice
- ¿Qué es Mock Service Worker (MSW)?
- Ejemplo práctico
- Generando datos aleatorios con Faker
- Object Mother
- Profundizando en los handler
- Conclusión
- Enlaces
1.- ¿Qué es Mock Service Worker (MSW)?
MSW nos permite crear una simulación de APIs para navegadores y para Node.js. Con MSW podemos hacer que nuestro proyecto apunte a una API que nos devuelva los datos que nosotros queremos e incluso hacer que nos devuelva errores, para así poder testear nuestras APIs de una forma real.
Además podemos añadir el retardo de respuesta que queramos para simular que el servidor tarda en respondernos y así comprobar que mostramos por ejemplo los spinners de carga e incluso podemos capturar los datos que mandamos a la API por parámetros y trabajar con esos parámetros.
Todo esto se hace de una forma muy sencilla la cual vamos a ver más adelante con un ejemplo práctico.
2.- Ejemplo práctico
Ya sabemos que es MSW pero ahora nos falta ver cómo funciona de forma práctica y qué mejor forma que con un ejemplo.
Para ello lo primero que voy a hacer es crear un proyecto nuevo en Angular 18 y añadirle una configuración de entorno para trabajar en local.
Creando el proyecto con Angular
En este tutorial no vamos a entrar a fondo en qué es Angular ni en cómo programar con este framework, pero si que os voy a enseñar los pasos que he ido siguiendo yo para tener nuestro primer proyecto con MSW funcionando.
Lo primero es crear un proyecto base y para ello lo primero es instalar Angular CLI:
Y a continuación creamos el proyecto Angular:
Listo, ya tenemos un proyecto base con el que vamos a trabajar, el cual puedes lanzar con el siguiente comando para ver que todo funciona correctamente:
Añadiendo la configuración de entorno
Queremos que nuestra aplicación apunte a nuestro MSW solo cuando estamos desarrollando y no cuando la aplicación está en producción, por ello tenemos que preparar el terreno para hacer que nuestro MSW se inicialice solo cuando estamos desarrollando. Para ello voy a crear 2 entornos, uno para desarrollo y otro para producción.
Como veréis en el ejemplo, en el ejemplo voy a poner un parámetro de entorno en el que voy a decir si la aplicación está en producción o no con un booleano y según esto voy a hacer que apunte la aplicación a nuestro MSW o al servidor backend de producción. En estas variables de entorno podemos poner cualquier parámetro que necesitemos, por ejemplo incluso podemos definir la URL de la API y tener un entorno de producción con la URL de producción, otro entorno de desarrollo con la API de desarrollo y otro entorno local con nuestro MSW.
Para no liarnos mucho, en mis archivos de entorno solo voy a poner un parámetro que indique si la aplicación está o no en producción y según eso voy a poner que se inicie o no el MSW.
Lo primero es ejecutar el comando que nos ofrece angular para crear los entornos:
Este comando va a generarnos una carpeta llamada environments dentro de ./src con 2 archivos, environment.ts y environment.development.ts. Ambos archivos contienen una constante que exportan con un objeto vacío:
Dentro de este objeto en los 2 archivos vamos a añadir una variable de entorno con un valor booleano para detectar si estamos o no en producción. El valor true será para el archivo environment.ts y el false para el environment.development.ts
Ahora, vamos a duplicar el archivo main.ts que se encuentra en ./src/main.ts y lo vamos a renombrar a main-dev.ts para aquí hacer la configuración que necesitamos para inicializar el MSW en desarrollo.
Una vez hecho todo esto, tenemos que crear un archivo en la raíz del proyecto llamado tsconfig.app.dev.json que extienda de tsconfig.json al que le añadiremos este archivo que hemos creado. Si se elimina o se renombre el archivo main-dev.ts obtendremos un error y así sabremos que este nos falta. En este .json de configuración podemos añadir las opciones que necesitemos para nuestro desarrollo.
Ahora, en nuestro angular.json tenemos que añadir algunas propiedades, para que cuando lancemos el proyecto en local, se inicie el archivo main-dev.ts, reconozca el archivo tsconfig.app.dev.json y reemplace el environment de producción con el de desarrollo.
Por último tenemos, en el apartado de scripts en nuestro package.json vamos a modificar el de start para que use la configuración de producción y vamos a añadir uno para trabajar en local indicando que configuración de nuestro angular.json tiene que ejecutar:
Ya estaría toda la configuración de entorno lista, pero si ejecutamos npm run start:local veremos un error en consola…
Para solucionar esto, tal y como el error indica, tenemos que ejecutar el comando:
Se creará un archivo llamado mockServiceWorker.js dentro de ./src y este tendremos que añadirlo a nuestros assets en nuestro angular.js
Creando una llamada al MSW
Como buena práctica, es necesario crear un handler por cada entidad que tengamos en nuestra API. Por ello voy a crear un ejemplo haciendo un handler para la entidad user. A este archivo le voy a llamar user.handler.ts y lo voy a poner en la ruta src/core/devtools/mock-server/user.handler.ts
Aquí lo que definimos es un array de endpoints, definiendo si es de tipo GET, POST, PUT, DELETE etc y a continuación la url que queremos mockear. En la función de flecha definimos la respuesta que queremos, que en este caso va a ser un JSON con unos valores estáticos. Más adelante entraremos más en profundidad en las opciones que tenemos en el handler.
Ahora necesitamos que nuestro MSW conozca este handler que hemos creado y para ello necesitamos crear un archivo que de forma estándar se le llama browser.ts y lo voy a crear en la ruta src/core/devtools/mock-server/browser.ts
Aquí importo todos los handlers creados, los añado a un array y este se lo paso como parámetro a la función setupWorker del MSW:
Por último en el archivo main-dev.ts que creamos anteriormente, importo esta constante que acabamos de definir e inicializamos todo si la variable de entorno “production” está a false.
Con esto ya tendremos en escucha nuestro MSW para que cuando se haga una petición GET a la url https://example.com/user nos responda con los datos que hemos definido.
En este punto solo falta que en nuestro componente hagamos la petición que comento y hagamos lo que veamos oportuno con los datos de la respuesta, en mi caso lo guardo en un signal y los muestro por pantalla en el html. Todo esto lo tenéis de forma detallada en el link a GitHub que dejo al final de este tutorial.
3.- Generando datos aleatorios con Faker
Tanto a la hora generar respuestas en nuestro MSW como cuando hacemos tests, lo mejor para contemplar un amplio abanico de escenarios es usar datos lo más realistas posibles y para ello en este tutorial voy a usar la librería de Faker.
Faker es una librería para generar datos aleatorios de todo tipo, lo mismo te genera datos de una persona como puede ser un nombre o un email hasta generar datos de animales, vehículos, ips, avatares etc
¿Cómo instalar Faker?
Para instalar Faker, solo tienes que ejecutar el siguiente comando:
Utilizando la librería Faker
Como ya hemos visto, la librería Faker sirve para generar muchos datos, pero en mi caso solo necesito generar un nombre, un apellido y un uuid. Es por ello que he hecho una clase que uso como adaptador en la cual me he creado 3 métodos que devuelven estos datos. Es posible que en el futuro esta librería se quede obsoleta o que encuentre otra que me guste más, entonces por esto tomo la decisión de hacer esta clase adaptador que será la que usaré a posteriori. Si en un futuro quiero cambiar de librería, solo tendría que modificar esta clase que voy a crear y no todos los sitios del proyecto en los que use la librería.
He llamado a mi clase Fakerjs y le he implementado una clase abstracta con los métodos que voy a usar. Finalmente he añadido estos tres métodos llamando a los métodos correspondientes de la librería Faker. Todo esto lo he hecho en la ruta: src/shared/infrastructure/test/fakerjs.ts
4.- Object Mother
¿Qué es un Object Mother?
Los Object Mother son un patrón de diseño que se usa para crear objetos con datos de prueba. Estos se suelen usar para los tests pero además en nuestro caso los vamos a utilizar para obtener datos de prueba en nuestro MSW.
Por lo general, los Object Mother son clases con métodos estáticos que devuelven objetos o clases con datos preestablecidos pero a los cuales también se le pasan datos (si fuera necesario) para crear overrides de los datos preestablecidos.
Utilizando el Object Mother en el proyecto
En este caso, voy a combinar el Object Mother con la librería faker para poder tener objetos con datos aleatorios.
Primero voy a definir una clase abstracta que importe la clase Fakerjs que he creado anteriormente en la ruta src/shared/domain/test/object-mother.ts
Ahora en la ruta src/app/pages/components/test/user.object.ts creo el Object Mother para generar datos de un usuario:
Integrando el Object Mother con el MSW
Para que la petición /user devuelva los datos aleatorios de nuestro usuario, solo hay que importar el UserMother que he creado y llamar a su método .dto() Por cierto, por si te lo preguntas dto significa Data Transfer Object. Para esto en el archivo user.handler.ts solo hay que modificar el http.get que teníamos de la siguiente forma:
5.- Profundizando en los handler
MSW tiene muchas funcionalidades muy interesantes que te animo a descubrir en su documentación oficial. Aquí nos vamos a centrar en algunos puntos que creo que serán los que más usemos.
Código de respuesta: Por defecto todas las peticiones que tenemos definidas en nuestro handler devuelve un código 200, pero ¿y si queremos que la petición falle para ver si nuestro programa funciona bien en este caso? Para ello podemos definir en la respuesta el código que queremos, por ejemplo un error 400. Para ello solo tenemos que pasar como segundo parámetro al método, un objeto con status y el código que queramos.
Ejemplo:
Añadir delay: Vas a comprobar que el handler responde casi al instante, pero normalmente un servidor no va a responder tan rápido e incluso habrá momentos en los que puede que se quede colgado. Para esto podemos usar antes del return una llamada a la función delay de msw pasándole el tiempo que queremos que espere (en milisegundos)
Ejemplo:
Obtener los valores por parámetro: Supongamos que queremos consultar el usuario con ID 1, lo lógico sería que en la respuesta de nuestro handler viniera este ID para el usuario. Para ello lo primero es decirle al handler que ahora va a recibir un parámetro y al que vamos a llamar id. Solo tenemos que modificar la url y añadirle /:id.
Ejemplo:
Ahora para recuperar ese ID dentro de la función de flecha, tenemos que recuperarlo del primer parámetro que nos devuelve esta y de ahí acceder a la propiedad “params” y en concreto a la key que hemos definido como “id”.
Ejemplo:
6.- Conclusión
En conclusión considero que MSW es muy útil para trabajar sin tener que depender del equipo de back, teniendo en cuenta eso sí los contratos de las APIs. Es una herramienta muy útil y con muchas funcionalidades que ayudan enormemente al desarrollo del front. Esto acompañado de los Object Mother con la librería Faker (o cualquier otra que te guste), considero que es la combinación perfecta para trabajar con unas respuestas de APIs lo más parecidas a un entorno real posible.
Me gustaría hacer una mención especial a mi compañero Valentín Valverde Romero que es quien en su día me enseñó todo esto que hoy os estoy mostrando y mucho más. Por aquí os dejo su Linkedin por si queréis saber más de él.
7.- Enlaces
GitHub del proyecto de ejemplo: https://github.com/andresautentia/msw-tutorial
Documentación oficial de MSW: https://mswjs.io/
Ejemplos oficiales de MSW: https://github.com/mswjs/examples/tree/main/examples
Documentación de la librería Faker: https://fakerjs.dev/