Mirage JS Deep Dive: comprensione di fabbriche, dispositivi e serializzatori (parte 2)

Pubblicato: 2022-03-10
Riassunto rapido ↬ In questa seconda parte della serie Mirage JS Deep Dive, esamineremo le fabbriche, le apparecchiature e i serializzatori di Mirage JS. Vedremo come abilitano il mocking rapido delle API utilizzando Mirage.

Nel precedente articolo di questa serie, abbiamo sottovalutato i modelli e le associazioni in relazione a Mirage. Ho spiegato che i modelli ci consentono di creare dati fittizi dinamici che Mirage servirebbe alla nostra applicazione quando effettua una richiesta ai nostri endpoint fittizi. In questo articolo, esamineremo altre tre funzionalità di Mirage che consentono una presa in giro dell'API ancora più rapida. Entriamo subito!

Nota : consiglio vivamente di leggere i miei primi due articoli se non si ha un'idea davvero solida di ciò che verrebbe discusso qui. Puoi comunque seguire e fare riferimento agli articoli precedenti quando necessario.

  • Configurazione di API Mocking con Mirage JS e Vue
  • Modelli e associazioni Mirage JS

Fabbriche

In un articolo precedente, ho spiegato come viene utilizzato Mirage JS per deridere l'API di back-end, ora supponiamo di deridere una risorsa di prodotto in Mirage. Per ottenere ciò, creeremo un gestore di route che sarà responsabile dell'intercettazione delle richieste a un particolare endpoint e, in questo caso, l'endpoint è api/products . Il gestore del percorso che creiamo restituirà tutti i prodotti. Di seguito è riportato il codice per ottenere ciò in Mirage:

 import { Server, Model } from 'miragejs'; new Server({ models: { product: Model, }, routes() { this.namespace = "api"; this.get('products', (schema, request) => { return schema.products.all() }) } }); },

L'output di quanto sopra sarebbe:

 { "products": [] }

Vediamo dall'output sopra che la risorsa del prodotto è vuota. Ciò è tuttavia previsto poiché non abbiamo ancora creato alcun record.

Suggerimento professionale : Mirage fornisce la scorciatoia necessaria per gli endpoint API convenzionali. Quindi il gestore del percorso sopra potrebbe anche essere breve come: this.get('/products') .

Altro dopo il salto! Continua a leggere sotto ↓

Creiamo i record del modello di product da archiviare nel database Mirage utilizzando il metodo seeds sulla nostra istanza Server :

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

L'output:

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

Come puoi vedere sopra, quando la nostra applicazione frontend effettua una richiesta a /api/products , riceverà una raccolta di prodotti come definito nel metodo seeds .

L'uso del metodo seeds per eseguire il seeding del database di Mirage è un passaggio dal dover creare manualmente ogni voce come oggetto. Tuttavia, non sarebbe pratico creare 1000 (o un milione) di nuovi record di prodotti utilizzando il modello sopra. Da qui la necessità di fabbriche .

Spiegazione delle fabbriche

Le fabbriche sono un modo più veloce per creare nuovi record di database. Ci consentono di creare rapidamente più record di un particolare modello con variazioni da archiviare nel database Mirage JS.

Le fabbriche sono anche oggetti che semplificano la generazione di dati dall'aspetto realistico senza dover eseguire il seeding di tali dati individualmente. Le fabbriche sono più di ricette o progetti per la creazione di record da modelli.

Creare una fabbrica

Esaminiamo una fabbrica creandone una. La fabbrica che creeremo verrà utilizzata come modello per la creazione di nuovi prodotti nel nostro database 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({}) } })

Da quanto sopra, vedresti che abbiamo aggiunto una proprietà factories alla nostra istanza Server e ne definiamo un'altra proprietà che per convenzione ha lo stesso nome del modello per cui vogliamo creare una factory, in questo caso, quel modello è il modello product . Lo snippet sopra raffigura lo schema che seguiresti durante la creazione di fabbriche in Mirage JS.

Sebbene disponiamo di una fabbrica per il modello del product , in realtà non abbiamo aggiunto proprietà ad esso. Le proprietà di una fabbrica possono essere tipi semplici come stringhe , booleane o numeri o funzioni che restituiscono dati dinamici come vedremmo nell'implementazione completa della nostra nuova fabbrica di prodotti di seguito:

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

Nello snippet di codice sopra, stiamo specificando una logica javascript tramite Math.random per creare dati dinamici ogni volta che la fabbrica viene utilizzata per creare un nuovo record di prodotto. Questo mostra la forza e la flessibilità delle Fabbriche.

Creiamo un prodotto utilizzando la fabbrica che abbiamo definito sopra. Per fare ciò, chiamiamo server.create e passiamo il nome del modello ( product ) come stringa. Mirage creerà quindi un nuovo record di un prodotto utilizzando la fabbrica di prodotti che abbiamo definito. Il codice che ti serve per farlo è il seguente:

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

Suggerimento per professionisti : puoi eseguire console.log(server.db.dump()) per vedere i record nel database di Mirage.

Un nuovo record simile a quello seguente è stato creato e archiviato nel database di Mirage.

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

Fabbriche prioritarie

Possiamo sovrascrivere alcuni o più valori forniti da una fabbrica passandoli esplicitamente in questo modo:

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

Il record risultante sarebbe simile a:

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

creareLista

Con una factory in atto, possiamo usare un altro metodo sull'oggetto server chiamato createList . Questo metodo consente la creazione di più record di un particolare modello passando il nome del modello e il numero di record che si desidera creare. Di seguito è il suo utilizzo:

 server.createList("product", 10)

o

 server.createList("product", 1000)

Come noterai, il metodo createList sopra accetta due argomenti: il nome del modello come stringa e un numero intero positivo diverso da zero che rappresenta il numero di record da creare. Quindi, da quanto sopra, abbiamo appena creato 500 record di prodotti! Questo modello è utile per i test dell'interfaccia utente, come vedrai in un prossimo articolo di questa serie.

Infissi

Nel test del software, un dispositivo di test o un dispositivo di test è uno stato di un insieme o una raccolta di oggetti che fungono da base per l'esecuzione dei test. Lo scopo principale di un dispositivo è garantire che l'ambiente di test sia ben noto per rendere i risultati ripetibili.

Mirage ti consente di creare infissi e usarli per seminare il tuo database con i dati iniziali.

Nota : si consiglia di utilizzare le fabbriche 9 volte su 10, poiché rendono le tue simulazioni più gestibili.

Creazione di un dispositivo

Creiamo un semplice dispositivo per caricare i dati nel nostro database:

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

I dati di cui sopra vengono caricati automaticamente nel database come dati iniziali di Mirage. Tuttavia, se hai una funzione seed definita, Mirage ignorerebbe il tuo dispositivo con i presupposti che volevi che venisse sovrascritto e utilizzerà invece le factory per eseguire il seeding dei tuoi dati.

Infissi in congiunzione con le fabbriche

Mirage prevede l'utilizzo di Fixtures insieme alle Fabbriche. Puoi ottenerlo chiamando server.loadFixtures() . Per esempio:

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

File di fissaggio

Idealmente, vorresti creare i tuoi dispositivi in ​​un file separato da server.js e importarlo. Ad esempio puoi creare una directory chiamata fixtures e in essa creare products.js . In products.js aggiungi:

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

Quindi in server.js importa e usa il dispositivo di prodotti in questo modo:

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

Sto usando la scorciatoia della proprietà ES6 per assegnare l'array dei prodotti importati alla proprietà dei products dell'oggetto fixtures.

È degno di nota il fatto che le apparecchiature verrebbero ignorate da Mirage JS durante i test a meno che tu non lo dica esplicitamente di non farlo usando server.loadFixtures()

Fabbriche contro impianti

A mio parere, dovresti astenerti dall'usare gli infissi tranne che hai un caso d'uso particolare in cui sono più adatti delle fabbriche. I dispositivi tendono ad essere più dettagliati mentre le fabbriche sono più veloci e comportano meno sequenze di tasti.

Serializzatori

È importante restituire un payload JSON previsto per il frontend, quindi serializers .

Un serializzatore è un oggetto responsabile della trasformazione di un **Model** o **Collection** restituito dai gestori di route in un payload JSON formattato nel modo previsto dall'app frontend.

Mirage Docs

Prendiamo ad esempio questo gestore di percorso:

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

Un serializzatore è responsabile di trasformare la risposta in qualcosa del genere:

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

Serializzatori integrati Mirage JS

Per lavorare con i serializzatori Mirage JS, dovresti scegliere con quale serializzatore integrato iniziare. Questa decisione sarebbe influenzata dal tipo di JSON che il tuo back-end invierebbe alla tua applicazione front-end. Mirage viene fornito con i seguenti serializzatori:

  • JSONAPISerializer
    Questo serializzatore segue le specifiche JSON:API.
  • ActiveModelSerializer
    Questo serializzatore ha lo scopo di imitare le API che assomigliano alle API Rails create con la gem active_model_serializer.
  • RestSerializer
    RestSerializer è il serializzatore Mirage JS "catch all" per altre API comuni.

Definizione del serializzatore

Per definire una serializzazione, importare il serializzatore appropriato, ad esempio RestSerializer da miragejs in questo modo:

 import { Server, RestSerializer } from "miragejs"

Quindi nell'istanza del Server :

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

RestSerializer è utilizzato da Mirage JS per impostazione predefinita. Quindi è ridondante impostarlo esplicitamente. Il frammento di cui sopra è a scopo esemplare.

Vediamo l'output di JSONAPISerializer e ActiveModelSerializer sullo stesso gestore di route definito sopra

Serializzatore JSONAPI

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

L'output:

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

Serializzatore modello attivo

Per vedere l'ActiveModelSerializer al lavoro, modificherei la dichiarazione di category nella fabbrica dei prodotti in:

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

Tutto quello che ho fatto è stato cambiare il nome della proprietà in productCategory per mostrare come il serializzatore lo gestirebbe.

Quindi, definiamo il serializzatore ActiveModelSerializer in questo modo:

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

Il serializzatore trasforma il JSON restituito come:

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

Noterai che productCategory è stato trasformato in product_category che è conforme alla gemma active_model_serializer dell'ecosistema Ruby.

Personalizzazione dei serializzatori

Mirage offre la possibilità di personalizzare un serializzatore. Supponiamo che la tua applicazione richieda che i nomi degli attributi siano camelcase, puoi sovrascrivere RestSerializer per ottenerlo. Utilizzeremmo la libreria di utilità lodash :

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

Questo dovrebbe produrre JSON del modulo:

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

Avvolgendo

L'hai fatta! Si spera che tu abbia una comprensione più profonda di Mirage tramite questo articolo e che tu abbia anche visto come l'utilizzo di fabbriche, dispositivi e serializzatori ti consentirebbe di creare simulazioni API più simili alla produzione con Mirage.

  • Parte 1: Comprensione dei modelli e delle associazioni Mirage JS
  • Parte 2: Comprensione di fabbriche, dispositivi e serializzatori
  • Parte 3: Capire i tempi, la risposta e il passthrough
  • Parte 4: utilizzo di Mirage JS e Cypress per i test dell'interfaccia utente