Mirage JS Deep Dive : Comprendre les usines, les luminaires et les sérialiseurs (Partie 2)

Publié: 2022-03-10
Résumé rapide ↬ Dans cette deuxième partie de la série Mirage JS Deep Dive, nous examinerons les usines, les appareils et les sérialiseurs de Mirage JS. Nous verrons comment ils permettent une simulation rapide de l'API à l'aide de Mirage.

Dans l'article précédent de cette série, nous avons sous-étudié les modèles et les associations en ce qui concerne Mirage. J'ai expliqué que les modèles nous permettaient de créer des données fictives dynamiques que Mirage servirait à notre application lorsqu'il adresserait une demande à nos points de terminaison fictifs. Dans cet article, nous examinerons trois autres fonctionnalités de Mirage qui permettent une moquerie d'API encore plus rapide. Plongeons dedans !

Note : Je vous recommande fortement de lire mes deux premiers articles si vous n'avez pas une bonne idée de ce qui serait discuté ici. Vous pouvez cependant toujours suivre et référencer les articles précédents si nécessaire.

  • Configuration de l'API Mocking avec Mirage JS et Vue
  • Mirage JS Modèles Et Associations

Des usines

Dans un article précédent, j'ai expliqué comment Mirage JS est utilisé pour se moquer de l'API backend, supposons maintenant que nous nous moquons d'une ressource produit dans Mirage. Pour ce faire, nous créerions un gestionnaire de route qui sera responsable de l'interception des requêtes vers un point de terminaison particulier, et dans ce cas, le point de terminaison est api/products . Le gestionnaire de route que nous créons renverra tous les produits. Voici le code pour y parvenir dans 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 sortie de ce qui précède serait :

 { "products": [] }

Nous voyons d'après la sortie ci-dessus que la ressource produit est vide. Ceci est cependant attendu car nous n'avons pas encore créé d'enregistrements.

Conseil de pro : Mirage fournit le raccourci nécessaire pour les points de terminaison d'API conventionnels. Ainsi, le gestionnaire de route ci-dessus pourrait également être aussi court que : this.get('/products') .

Plus après saut! Continuez à lire ci-dessous ↓

Créons des enregistrements du modèle de product à stocker dans la base de données Mirage en utilisant la méthode des seeds sur notre instance de Server :

 seeds(server) { server.create('product', { name: 'Gemini Jacket' }) server.create('product', { name: 'Hansel Jeans' }) },

Le résultat:

 { "products": [ { "name": "Gemini Jacket", "id": "1" }, { "name": "Hansel Jeans", "id": "2" } ] }

Comme vous pouvez le voir ci-dessus, lorsque notre application frontale fait une requête à /api/products , elle récupère une collection de produits telle que définie dans la méthode seeds .

L'utilisation de la méthode des seeds pour ensemencer la base de données de Mirage est une étape par rapport à la création manuelle de chaque entrée en tant qu'objet. Cependant, il ne serait pas pratique de créer 1000 (ou un million) nouveaux enregistrements de produits en utilisant le modèle ci-dessus. D'où le besoin d' usines .

Les usines expliquées

Les usines sont un moyen plus rapide de créer de nouveaux enregistrements de base de données. Ils nous permettent de créer rapidement plusieurs enregistrements d'un modèle particulier avec des variations à stocker dans la base de données Mirage JS.

Les usines sont également des objets qui facilitent la génération de données réalistes sans avoir à ensemencer ces données individuellement. Les usines sont plus des recettes ou des plans pour créer des enregistrements à partir de modèles.

Créer une usine

Examinons une Usine en en créant une. L'usine que nous créerions servira de modèle pour créer de nouveaux produits dans notre base de données 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({}) } })

D'après ce qui précède, vous verriez que nous avons ajouté une propriété factories à notre instance de Server et défini une autre propriété à l'intérieur qui, par convention, porte le même nom que le modèle pour lequel nous voulons créer une usine, dans ce cas, ce modèle est le modèle product . L'extrait ci-dessus décrit le modèle que vous suivrez lors de la création d'usines dans Mirage JS.

Bien que nous ayons une usine pour le modèle de product , nous n'y avons vraiment pas ajouté de propriétés. Les propriétés d'une fabrique peuvent être des types simples comme des chaînes , des booléens ou des nombres , ou des fonctions qui renvoient des données dynamiques comme nous le verrions dans l'implémentation complète de notre nouvelle fabrique de produits ci-dessous :

 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; }, }), }, })

Dans l'extrait de code ci-dessus, nous spécifions une logique javascript via Math.random pour créer des données dynamiques chaque fois que l'usine est utilisée pour créer un nouvel enregistrement de produit. Cela montre la force et la flexibilité des usines.

Créons un produit en utilisant l'usine que nous avons définie ci-dessus. Pour ce faire, nous appelons server.create et transmettons le nom du modèle ( product ) sous forme de chaîne. Mirage créera alors un nouvel enregistrement d'un produit en utilisant l'usine de produits que nous avons définie. Le code dont vous avez besoin pour ce faire est le suivant :

 new Server({ seeds(server) { server.create("product") } })

Astuce Pro : Vous pouvez exécuter console.log(server.db.dump()) pour voir les enregistrements dans la base de données de Mirage.

Un nouvel enregistrement similaire à celui ci-dessous a été créé et stocké dans la base de données Mirage.

 { "products": [ { "rating": 3, "category": "Computing", "price": "$739", "name": "Awesome Product 0", "id": "1" } ] }

Remplacement des usines

Nous pouvons remplacer certaines ou plusieurs des valeurs fournies par une fabrique en les transmettant explicitement comme ceci :

 server.create("product", {name: "Yet Another Product", rating: 5, category: "Fashion" })

L'enregistrement résultant ressemblerait à :

 { "products": [ { "rating": 5, "category": "Fashion", "price": "$782", "name": "Yet Another Product", "id": "1" } ] }

créer une liste

Avec une usine en place, nous pouvons utiliser une autre méthode sur l'objet serveur appelée createList . Cette méthode permet la création de plusieurs enregistrements d'un modèle particulier en transmettant le nom du modèle et le nombre d'enregistrements que vous souhaitez créer. Ci-dessous son utilisation :

 server.createList("product", 10)

Ou

 server.createList("product", 1000)

Comme vous le constaterez, la méthode createList ci-dessus prend deux arguments : le nom du modèle sous forme de chaîne et un entier positif non nul représentant le nombre d'enregistrements à créer. Donc, à partir de ce qui précède, nous venons de créer 500 enregistrements de produits ! Ce modèle est utile pour les tests d'interface utilisateur, comme vous le verrez dans un prochain article de cette série.

Agencements

Dans les tests de logiciels, un appareil de test ou un appareil est un état d'un ensemble ou d'une collection d'objets qui servent de référence pour l'exécution des tests. L'objectif principal d'un montage est de s'assurer que l'environnement de test est bien connu afin de rendre les résultats reproductibles.

Mirage vous permet de créer des appareils et de les utiliser pour alimenter votre base de données avec les données initiales.

Remarque : Il est recommandé d'utiliser des usines 9 fois sur 10, car elles rendent vos maquettes plus maintenables.

Création d'un luminaire

Créons un appareil simple pour charger des données dans notre base de données :

 fixtures: { products: [ { id: 1, name: 'T-shirts' }, { id: 2, name: 'Work Jeans' }, ], },

Les données ci-dessus sont automatiquement chargées dans la base de données en tant que données initiales de Mirage. Cependant, si vous avez défini une fonction d'amorçage, Mirage ignorera votre appareil avec les hypothèses que vous vouliez qu'il soit remplacé et utilisera à la place des usines pour amorcer vos données.

Luminaires en collaboration avec les usines

Mirage vous permet d'utiliser des luminaires aux côtés des usines. Vous pouvez y parvenir en appelant server.loadFixtures() . Par exemple:

 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") },

Fichiers de luminaires

Idéalement, vous voudriez créer vos appareils dans un fichier séparé de server.js et l'importer. Par exemple, vous pouvez créer un répertoire appelé fixtures et y créer products.js . Dans products.js ajouter :

 // <PROJECT-ROOT>/fixtures/products.js export default [ { id: 1, name: 'iPhone 7' }, { id: 2, name: 'Smart TV' }, { id: 3, name: 'Pressing Iron' }, ];

Ensuite, dans server.js , importez et utilisez le produit comme suit :

 import products from './fixtures/products'; fixtures: { products, },

J'utilise le raccourci de la propriété ES6 afin d'affecter le tableau de produits importé à la propriété products de l'objet fixtures.

Il convient de mentionner que les luminaires seraient ignorés par Mirage JS lors des tests, sauf si vous lui dites explicitement de ne pas le faire en utilisant server.loadFixtures()

Usines vs agencements

À mon avis, vous devriez vous abstenir d'utiliser des luminaires, sauf si vous avez un cas d'utilisation particulier où ils sont plus adaptés que les usines. Les montages ont tendance à être plus verbeux tandis que les usines sont plus rapides et impliquent moins de frappes.

Sérialiseurs

Il est important de renvoyer une charge utile JSON qui est attendue sur le frontend, donc les sérialiseurs .

Un sérialiseur est un objet chargé de transformer un **modèle** ou une **collection** renvoyé par vos gestionnaires de route en une charge utile JSON formatée comme l'attend votre application frontale.

Documents Mirage

Prenons par exemple ce gestionnaire de route :

 this.get('products/:id', (schema, request) => { return schema.products.find(request.params.id); });

Un sérialiseur est chargé de transformer la réponse en quelque chose comme ceci :

 { "product": { "rating": 0, "category": "Baby Products", "price": "$654", "name": "Awesome Product 1", "id": "2" } }

Sérialiseurs intégrés Mirage JS

Pour travailler avec les sérialiseurs Mirage JS, vous devez choisir avec quel sérialiseur intégré commencer. Cette décision serait influencée par le type de JSON que votre backend enverrait éventuellement à votre application frontale. Mirage est fourni avec les sérialiseurs suivants :

  • JSONAPISerializer
    Ce sérialiseur suit la spécification JSON:API.
  • ActiveModelSerializer
    Ce sérialiseur est destiné à imiter les API qui ressemblent aux API Rails construites avec la gemme active_model_serializer.
  • RestSerializer
    Le RestSerializer est un sérialiseur "fourre-tout" Mirage JS pour d'autres API courantes.

Définition du sérialiseur

Pour définir une sérialisation, importez le sérialiseur approprié, par exemple RestSerializer à partir de miragejs , comme suit :

 import { Server, RestSerializer } from "miragejs"

Puis dans l'instance Server :

 new Server({ serializers: { application: RestSerializer, }, })

Le RestSerializer est utilisé par Mirage JS par défaut. Il est donc redondant de le définir explicitement. L'extrait ci-dessus est à des fins d'exemple.

Voyons la sortie de JSONAPISerializer et ActiveModelSerializer sur le même gestionnaire de route que nous avons défini ci-dessus

JSONAPISerializerJSONAPISerializer

 import { Server, JSONAPISerializer } from "miragejs" new Server({ serializers: { application: JSONAPISerializer, }, })

Le résultat:

 { "data": { "type": "products", "id": "2", "attributes": { "rating": 3, "category": "Electronics", "price": "$1711", "name": "Awesome Product 1" } } }

ActiveModelSerializer

Pour voir l'ActiveModelSerializer au travail, je modifierais la déclaration de category dans l'usine de produits pour :

 productCategory() { let categories = [ 'Electronics', 'Computing', 'Fashion', 'Gaming', 'Baby Products', ]; let randomCategoryIndex = Math.floor( Math.random() * categories.length ); let randomCategory = categories[randomCategoryIndex]; return randomCategory; },

Tout ce que j'ai fait était de changer le nom de la propriété en productCategory pour montrer comment le sérialiseur le gérerait.

Ensuite, nous définissons le sérialiseur ActiveModelSerializer comme suit :

 import { Server, ActiveModelSerializer } from "miragejs" new Server({ serializers: { application: ActiveModelSerializer, }, })

Le sérialiseur transforme le JSON renvoyé en :

 { "rating": 2, "product_category": "Computing", "price": "$64", "name": "Awesome Product 4", "id": "5" }

Vous remarquerez que productCategory a été transformé en product_category qui est conforme à la gemme active_model_serializer de l'écosystème Ruby.

Personnalisation des sérialiseurs

Mirage offre la possibilité de personnaliser un sérialiseur. Supposons que votre application nécessite que vos noms d'attributs soient en camel, vous pouvez remplacer RestSerializer pour y parvenir. Nous utiliserions la bibliothèque d'utilitaires lodash :

 import { RestSerializer } from 'miragejs'; import { camelCase, upperFirst } from 'lodash'; serializers: { application: RestSerializer.extend({ keyForAttribute(attr) { return upperFirst(camelCase(attr)); }, }), },

Cela devrait produire JSON de la forme :

 { "Rating": 5, "ProductCategory": "Fashion", "Price": "$1386", "Name": "Awesome Product 4", "Id": "5" }

Emballer

Tu l'as fait! J'espère que vous avez une meilleure compréhension de Mirage via cet article et que vous avez également vu comment l'utilisation d'usines, d'appareils et de sérialiseurs vous permettrait de créer des simulations d'API plus proches de la production avec Mirage.

  • Partie 1 : Comprendre les modèles et les associations de Mirage JS
  • Partie 2 : Comprendre les usines, les montages et les sérialiseurs
  • Partie 3 : Comprendre le timing, la réponse et le passthrough
  • Partie 4 : Utilisation de Mirage JS et Cypress pour les tests d'interface utilisateur