Mirage JS Deep Dive: comprensión de fábricas, accesorios y serializadores (parte 2)
Publicado: 2022-03-10En el artículo anterior de esta serie, estudiamos Modelos y Asociaciones en relación con Mirage. Expliqué que los modelos nos permiten crear datos simulados dinámicos que Mirage entregaría a nuestra aplicación cuando realiza una solicitud a nuestros puntos finales simulados. En este artículo, veremos otras tres funciones de Mirage que permiten una simulación de API aún más rápida. ¡Vamos a sumergirnos!
Nota : Recomiendo encarecidamente leer mis dos primeros artículos si no es necesario para tener una idea sólida de lo que se discutirá aquí. Sin embargo, aún puede seguir y hacer referencia a los artículos anteriores cuando sea necesario.
- Configuración de simulación de API con Mirage JS y Vue
- Modelos y asociaciones de Mirage JS
Suerte
En un artículo anterior, expliqué cómo se usa Mirage JS para simular la API de back-end, ahora supongamos que estamos simulando un recurso de producto en Mirage. Para lograr esto, crearíamos un controlador de ruta que se encargará de interceptar las solicitudes a un punto final en particular, y en este caso, el punto final es api/products
. El controlador de ruta que creamos devolverá todos los productos. A continuación se muestra el código para lograr esto en Mirage:
import { Server, Model } from 'miragejs'; new Server({ models: { product: Model, }, routes() { this.namespace = "api"; this.get('products', (schema, request) => { return schema.products.all() }) } }); },
La salida de lo anterior sería:
{ "products": [] }
Vemos en el resultado anterior que el recurso del producto está vacío. Sin embargo, esto es de esperar ya que aún no hemos creado ningún registro.
Sugerencia profesional : Mirage proporciona la abreviatura necesaria para los puntos finales de API convencionales. Entonces, el controlador de ruta anterior también podría ser tan corto como: this.get('/products')
.
Vamos a crear registros del modelo de product
que se almacenará en la base de datos de Mirage utilizando el método de seeds
en nuestra instancia de Server
:
seeds(server) { server.create('product', { name: 'Gemini Jacket' }) server.create('product', { name: 'Hansel Jeans' }) },
La salida:
{ "products": [ { "name": "Gemini Jacket", "id": "1" }, { "name": "Hansel Jeans", "id": "2" } ] }
Como puede ver arriba, cuando nuestra aplicación frontend realiza una solicitud a /api/products
, obtendrá una colección de productos como se define en el método de seeds
.
Usar el método de seeds
para sembrar la base de datos de Mirage es un paso más que tener que crear manualmente cada entrada como un objeto. Sin embargo, no sería práctico crear 1000 (o un millón) de nuevos registros de productos utilizando el patrón anterior. De ahí la necesidad de fábricas .
Fábricas explicadas
Las fábricas son una forma más rápida de crear nuevos registros de base de datos. Nos permiten crear rápidamente múltiples registros de un modelo en particular con variaciones para ser almacenados en la base de datos de Mirage JS.
Las fábricas también son objetos que facilitan la generación de datos de aspecto realista sin tener que sembrar esos datos individualmente. Las fábricas son más recetas o planos para crear registros a partir de modelos.
Crear una fábrica
Examinemos una Fábrica creando una. La fábrica que crearíamos se usará como modelo para crear nuevos productos en nuestra base de datos de Mirage JS.
import { Factory } from 'miragejs' new Server({ // including the model definition for a better understanding of what's going on models: { product: Model }, factories: { product: Factory.extend({}) } })
De lo anterior, verá que agregamos una propiedad de factories
a nuestra instancia de Server
y definimos otra propiedad dentro de ella que, por convención, tiene el mismo nombre que el modelo para el que queremos crear una fábrica, en este caso, ese modelo es el modelo de product
El fragmento anterior muestra el patrón que seguiría al crear fábricas en Mirage JS.
Aunque tenemos una fábrica para el modelo del product
, realmente no le hemos agregado propiedades. Las propiedades de una fábrica pueden ser tipos simples como cadenas , booleanos o números , o funciones que devuelven datos dinámicos, como veremos en la implementación completa de nuestra nueva fábrica de productos a continuación:
import { Server, Model, Factory } from 'miragejs' new Server({ models: { product: Model }, factories: { product: Factory.extend({ name(i) { // i is the index of the record which will be auto incremented by Mirage JS return `Awesome Product ${i}`; // Awesome Product 1, Awesome Product 2, etc. }, price() { let minPrice = 20; let maxPrice = 2000; let randomPrice = Math.floor(Math.random() * (maxPrice - minPrice + 1)) + minPrice; return `$ ${randomPrice}`; }, category() { let categories = [ 'Electronics', 'Computing', 'Fashion', 'Gaming', 'Baby Products', ]; let randomCategoryIndex = Math.floor( Math.random() * categories.length ); let randomCategory = categories[randomCategoryIndex]; return randomCategory; }, rating() { let minRating = 0 let maxRating = 5 return Math.floor(Math.random() * (maxRating - minRating + 1)) + minRating; }, }), }, })
En el fragmento de código anterior, estamos especificando alguna lógica de javascript a través de Math.random
para crear datos dinámicos cada vez que se usa la fábrica para crear un nuevo registro de producto. Esto demuestra la fuerza y la flexibilidad de las fábricas.
Creemos un producto utilizando la fábrica que definimos anteriormente. Para hacerlo, llamamos a server.create
y pasamos el nombre del modelo ( product
) como una cadena. Mirage luego creará un nuevo registro de un producto utilizando la fábrica de productos que definimos. El código que necesitas para hacerlo es el siguiente:
new Server({ seeds(server) { server.create("product") } })
Consejo profesional : puede ejecutar console.log(server.db.dump())
para ver los registros en la base de datos de Mirage.
Se creó un nuevo registro similar al siguiente y se almacenó en la base de datos de Mirage.
{ "products": [ { "rating": 3, "category": "Computing", "price": "$739", "name": "Awesome Product 0", "id": "1" } ] }
Anulando fábricas
Podemos anular algunos o más de los valores proporcionados por una fábrica pasándolos explícitamente de la siguiente manera:
server.create("product", {name: "Yet Another Product", rating: 5, category: "Fashion" })
El registro resultante sería similar a:
{ "products": [ { "rating": 5, "category": "Fashion", "price": "$782", "name": "Yet Another Product", "id": "1" } ] }
Crear lista
Con una fábrica instalada, podemos usar otro método en el objeto del servidor llamado createList
. Este método permite la creación de múltiples registros de un modelo en particular al pasar el nombre del modelo y la cantidad de registros que desea crear. A continuación se muestra su uso:
server.createList("product", 10)
O

server.createList("product", 1000)
Como observará, el método anterior createList
toma dos argumentos: el nombre del modelo como una cadena y un entero positivo distinto de cero que representa la cantidad de registros que se crearán. Entonces, a partir de lo anterior, ¡acabamos de crear 500 registros de productos! Este patrón es útil para las pruebas de interfaz de usuario, como verá en un artículo futuro de esta serie.
Accesorios
En las pruebas de software, un dispositivo de prueba o accesorio es un estado de un conjunto o colección de objetos que sirven como línea de base para ejecutar pruebas. El objetivo principal de un accesorio es garantizar que el entorno de prueba sea bien conocido para que los resultados sean repetibles.
Mirage le permite crear accesorios y usarlos para sembrar su base de datos con datos iniciales.
Nota : Se recomienda usar fábricas 9 de cada 10 veces, ya que hacen que sus simulacros sean más fáciles de mantener.
Crear un accesorio
Vamos a crear un accesorio simple para cargar datos en nuestra base de datos:
fixtures: { products: [ { id: 1, name: 'T-shirts' }, { id: 2, name: 'Work Jeans' }, ], },
Los datos anteriores se cargan automáticamente en la base de datos como datos iniciales de Mirage. Sin embargo, si tiene definida una función de inicialización, Mirage ignoraría su accesorio con las suposiciones de que usted pretendía anularlo y, en su lugar, usaría fábricas para inicializar sus datos.
Accesorios en conjunto con fábricas
Mirage prevé el uso de accesorios junto con las fábricas. Puede lograr esto llamando a server.loadFixtures()
. Por ejemplo:
fixtures: { products: [ { id: 1, name: "iPhone 7" }, { id: 2, name: "Smart TV" }, { id: 3, name: "Pressing Iron" }, ], }, seeds(server) { // Permits both fixtures and factories to live side by side server.loadFixtures() server.create("product") },
Archivos de accesorios
Idealmente, desearía crear sus accesorios en un archivo separado de server.js
e importarlo. Por ejemplo, puede crear un directorio llamado fixtures
y en él crear products.js
. En products.js
agregue:
// <PROJECT-ROOT>/fixtures/products.js export default [ { id: 1, name: 'iPhone 7' }, { id: 2, name: 'Smart TV' }, { id: 3, name: 'Pressing Iron' }, ];
Luego, en server.js
importe y use el accesorio de productos así:
import products from './fixtures/products'; fixtures: { products, },
Estoy usando la propiedad abreviada ES6 para asignar la matriz de productos importada a la propiedad de products
del objeto de accesorios.
Vale la pena mencionar que Mirage JS ignorará los dispositivos durante las pruebas, excepto que usted le diga explícitamente que no lo haga mediante server.loadFixtures()
Fábricas vs accesorios
En mi opinión, debe abstenerse de usar accesorios, excepto que tenga un caso de uso particular en el que sean más adecuados que las fábricas. Los accesorios tienden a ser más detallados, mientras que las fábricas son más rápidas y requieren menos pulsaciones de teclas.
Serializadores
Es importante devolver una carga útil JSON que se espera para la interfaz, por lo tanto, serializadores .
Un serializador es un objeto que es responsable de transformar un **Modelo** o **Colección** que se devuelve de sus controladores de ruta en una carga útil JSON que tiene el formato esperado por su aplicación frontend.
Documentos de Mirage
Tomemos este controlador de ruta por ejemplo:
this.get('products/:id', (schema, request) => { return schema.products.find(request.params.id); });
Un serializador es responsable de transformar la respuesta a algo como esto:
{ "product": { "rating": 0, "category": "Baby Products", "price": "$654", "name": "Awesome Product 1", "id": "2" } }
Serializadores integrados de Mirage JS
Para trabajar con los serializadores Mirage JS, debe elegir con qué serializador integrado empezar. Esta decisión estaría influenciada por el tipo de JSON que su backend eventualmente enviaría a su aplicación front-end. Mirage viene incluido con los siguientes serializadores:
-
JSONAPISerializer
Este serializador sigue la especificación JSON:API. -
ActiveModelSerializer
Este serializador está diseñado para imitar las API que se asemejan a las API de Rails creadas con la gema active_model_serializer. -
RestSerializer
ElRestSerializer
es el serializador "catch all" de Mirage JS para otras API comunes.
Definición de serializador
Para definir una serialización, importe el serializador apropiado, por ejemplo RestSerializer
de miragejs
así:
import { Server, RestSerializer } from "miragejs"
Luego, en la instancia del Server
:
new Server({ serializers: { application: RestSerializer, }, })
Mirage JS utiliza RestSerializer
de forma predeterminada. Por lo tanto, es redundante configurarlo explícitamente. El fragmento anterior tiene fines ilustrativos.
Veamos el resultado de JSONAPISerializer
y ActiveModelSerializer
en el mismo controlador de ruta que definimos anteriormente.
JSONAPISerializador
import { Server, JSONAPISerializer } from "miragejs" new Server({ serializers: { application: JSONAPISerializer, }, })
La salida:
{ "data": { "type": "products", "id": "2", "attributes": { "rating": 3, "category": "Electronics", "price": "$1711", "name": "Awesome Product 1" } } }
ActiveModelSerializerActiveModelSerializerActiveModelSerializer
Para ver el ActiveModelSerializer en funcionamiento, modificaría la declaración de category
en la fábrica de productos para:
productCategory() { let categories = [ 'Electronics', 'Computing', 'Fashion', 'Gaming', 'Baby Products', ]; let randomCategoryIndex = Math.floor( Math.random() * categories.length ); let randomCategory = categories[randomCategoryIndex]; return randomCategory; },
Todo lo que hice fue cambiar el nombre de la propiedad a productCategory
para mostrar cómo lo manejaría el serializador.
Luego, definimos el serializador ActiveModelSerializer
así:
import { Server, ActiveModelSerializer } from "miragejs" new Server({ serializers: { application: ActiveModelSerializer, }, })
El serializador transforma el JSON devuelto como:
{ "rating": 2, "product_category": "Computing", "price": "$64", "name": "Awesome Product 4", "id": "5" }
Notará que productCategory
se ha transformado en product_category
, que se ajusta a la gema active_model_serializer del ecosistema Ruby.
Personalización de serializadores
Mirage ofrece la posibilidad de personalizar un serializador. Digamos que su aplicación requiere que los nombres de sus atributos estén en formato camel, puede anular RestSerializer
para lograrlo. Estaríamos utilizando la biblioteca de utilidades lodash
:
import { RestSerializer } from 'miragejs'; import { camelCase, upperFirst } from 'lodash'; serializers: { application: RestSerializer.extend({ keyForAttribute(attr) { return upperFirst(camelCase(attr)); }, }), },
Esto debería producir JSON de la forma:
{ "Rating": 5, "ProductCategory": "Fashion", "Price": "$1386", "Name": "Awesome Product 4", "Id": "5" }
Terminando
¡Lo hiciste! Es de esperar que tenga una comprensión más profunda de Mirage a través de este artículo y que también haya visto cómo utilizar fábricas, accesorios y serializadores le permitiría crear más simulacros de API de producción con Mirage.
- Parte 1: Comprensión de los modelos y asociaciones de Mirage JS
- Parte 2: Comprender las fábricas, los accesorios y los serializadores
- Parte 3: Comprender el tiempo, la respuesta y el traspaso
- Parte 4: uso de Mirage JS y Cypress para pruebas de interfaz de usuario