Mirage JS Deep Dive: Factorys, Fixtures und Serializer verstehen (Teil 2)
Veröffentlicht: 2022-03-10Im vorherigen Artikel dieser Serie haben wir Modelle und Assoziationen in Bezug auf Mirage zu wenig untersucht. Ich habe erklärt, dass Modelle es uns ermöglichen, dynamische Scheindaten zu erstellen, die Mirage unserer Anwendung bereitstellen würde, wenn sie eine Anfrage an unsere Scheinendpunkte stellt. In diesem Artikel werden wir uns drei weitere Mirage-Funktionen ansehen, die ein noch schnelleres API-Mocking ermöglichen. Lass uns gleich eintauchen!
Hinweis : Ich empfehle dringend, meine ersten beiden Artikel zu lesen, wenn Sie nicht wirklich wissen wollen, was hier besprochen wird. Sie können jedoch trotzdem mitverfolgen und bei Bedarf auf die vorherigen Artikel verweisen.
- API-Mocking mit Mirage JS und Vue einrichten
- Mirage JS-Modelle und -Verbände
Fabriken
In einem früheren Artikel habe ich erklärt, wie Mirage JS verwendet wird, um die Backend-API zu simulieren. Nehmen wir nun an, wir simulieren eine Produktressource in Mirage. Um dies zu erreichen, würden wir einen Routenhandler erstellen, der für das Abfangen von Anforderungen an einen bestimmten Endpunkt verantwortlich ist, und in diesem Fall ist der Endpunkt api/products
. Der von uns erstellte Routenhandler gibt alle Produkte zurück. Unten ist der Code, um dies in Mirage zu erreichen:
import { Server, Model } from 'miragejs'; new Server({ models: { product: Model, }, routes() { this.namespace = "api"; this.get('products', (schema, request) => { return schema.products.all() }) } }); },
Die Ausgabe des obigen wäre:
{ "products": [] }
Aus der obigen Ausgabe sehen wir, dass die Produktressource leer ist. Dies ist jedoch zu erwarten, da wir noch keine Datensätze erstellt haben.
Profi-Tipp : Mirage bietet eine Abkürzung, die für herkömmliche API-Endpunkte benötigt wird. Der obige Routenhandler könnte also auch so kurz sein wie: this.get('/products')
.
Lassen Sie uns Datensätze des product
erstellen, die in der Mirage-Datenbank gespeichert werden sollen, indem wir die seeds
-Methode auf unserer Server
verwenden:
seeds(server) { server.create('product', { name: 'Gemini Jacket' }) server.create('product', { name: 'Hansel Jeans' }) },
Die Ausgabe:
{ "products": [ { "name": "Gemini Jacket", "id": "1" }, { "name": "Hansel Jeans", "id": "2" } ] }
Wie Sie oben sehen können, erhält unsere Frontend-Anwendung, wenn sie eine Anfrage an /api/products
stellt, eine Sammlung von Produkten zurück, wie in der seeds
Methode definiert.
Die Verwendung der seeds
-Methode zum Seeden der Mirage-Datenbank ist ein Schritt von der Notwendigkeit, jeden Eintrag manuell als Objekt zu erstellen. Es wäre jedoch nicht praktikabel, 1000 (oder eine Million) neue Produktdatensätze mit dem obigen Muster zu erstellen. Daher die Notwendigkeit für Fabriken .
Fabriken erklärt
Fabriken sind eine schnellere Möglichkeit, neue Datenbankeinträge zu erstellen. Sie ermöglichen es uns, schnell mehrere Datensätze eines bestimmten Modells mit Variationen zu erstellen, die in der Mirage JS-Datenbank gespeichert werden.
Fabriken sind auch Objekte, die es einfach machen, realistisch aussehende Daten zu generieren, ohne diese Daten einzeln zu erstellen. Fabriken sind eher Rezepte oder Blaupausen zum Erstellen von Aufzeichnungen von Modellen.
Erstellen einer Fabrik
Lassen Sie uns eine Fabrik untersuchen, indem wir eine erstellen. Die Fabrik, die wir erstellen würden, wird als Blaupause für die Erstellung neuer Produkte in unserer Mirage JS-Datenbank verwendet.
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({}) } })
Aus dem Obigen sehen Sie, dass wir unserer Server
eine factories
-Eigenschaft hinzugefügt und darin eine andere Eigenschaft definiert haben, die per Konvention den gleichen Namen wie das Modell hat, für das wir eine Factory erstellen möchten, in diesem Fall ist dieses Modell die product
. Das obige Snippet zeigt das Muster, dem Sie beim Erstellen von Fabriken in Mirage JS folgen würden.
Obwohl wir eine Fabrik für das product
haben, haben wir ihm eigentlich keine Eigenschaften hinzugefügt. Die Eigenschaften einer Factory können einfache Typen wie strings , booleans oder numbers oder Funktionen sein, die dynamische Daten zurückgeben, wie wir in der vollständigen Implementierung unserer neuen Produkt-Factory unten sehen würden:
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; }, }), }, })
Im obigen Code-Snippet spezifizieren wir eine JavaScript-Logik über Math.random
, um jedes Mal dynamische Daten zu erstellen, wenn die Factory zum Erstellen eines neuen Produktdatensatzes verwendet wird. Dies zeigt die Stärke und Flexibilität von Fabriken.
Lassen Sie uns ein Produkt erstellen, das die oben definierte Fabrik verwendet. Dazu rufen wir server.create
und übergeben den Modellnamen ( product
) als String. Mirage erstellt dann einen neuen Datensatz eines Produkts unter Verwendung der von uns definierten Produktfabrik. Der Code, den Sie dazu benötigen, ist der folgende:
new Server({ seeds(server) { server.create("product") } })
Profi-Tipp : Sie können console.log(server.db.dump())
, um die Datensätze in der Mirage-Datenbank anzuzeigen.
Ein neuer Datensatz ähnlich dem untenstehenden wurde erstellt und in der Mirage-Datenbank gespeichert.
{ "products": [ { "rating": 3, "category": "Computing", "price": "$739", "name": "Awesome Product 0", "id": "1" } ] }
Fabriken überschreiben
Wir können einige oder mehrere der von einer Factory bereitgestellten Werte überschreiben, indem wir sie explizit wie folgt übergeben:
server.create("product", {name: "Yet Another Product", rating: 5, category: "Fashion" })
Der resultierende Datensatz wäre ähnlich wie:
{ "products": [ { "rating": 5, "category": "Fashion", "price": "$782", "name": "Yet Another Product", "id": "1" } ] }
Erstelle Liste
Wenn eine Factory vorhanden ist, können wir eine andere Methode namens createList
für das Serverobjekt verwenden. Diese Methode ermöglicht die Erstellung mehrerer Datensätze eines bestimmten Modells, indem der Modellname und die Anzahl der zu erstellenden Datensätze übergeben werden. Unten ist die Verwendung:
server.createList("product", 10)
Oder

server.createList("product", 1000)
Wie Sie sehen werden, nimmt die createList
Methode zwei Argumente entgegen: den Modellnamen als Zeichenfolge und eine positive Ganzzahl ungleich Null, die die Anzahl der zu erstellenden Datensätze darstellt. Aus dem oben Gesagten haben wir also gerade 500 Datensätze von Produkten erstellt! Dieses Muster ist für UI-Tests nützlich, wie Sie in einem zukünftigen Artikel dieser Reihe sehen werden.
Vorrichtungen
Beim Softwaretesten ist eine Testvorrichtung oder Vorrichtung ein Zustand eines Satzes oder einer Sammlung von Objekten, die als Grundlage für die Ausführung von Tests dienen. Der Hauptzweck einer Vorrichtung besteht darin, sicherzustellen, dass die Testumgebung gut bekannt ist, um die Ergebnisse wiederholbar zu machen.
Mit Mirage können Sie Fixtures erstellen und diese verwenden, um Ihre Datenbank mit Anfangsdaten zu füllen.
Hinweis : Es wird jedoch empfohlen, Fabriken 9 von 10 Mal zu verwenden, da sie Ihre Mocks wartungsfreundlicher machen.
Erstellen eines Fixtures
Lassen Sie uns ein einfaches Fixture erstellen, um Daten in unsere Datenbank zu laden:
fixtures: { products: [ { id: 1, name: 'T-shirts' }, { id: 2, name: 'Work Jeans' }, ], },
Die obigen Daten werden automatisch als Anfangsdaten von Mirage in die Datenbank geladen. Wenn Sie jedoch eine Seed-Funktion definiert haben, würde Mirage Ihr Fixture mit der Annahme ignorieren, dass es überschrieben werden soll, und stattdessen Factorys zum Seeding Ihrer Daten verwenden.
Vorrichtungen in Verbindung mit Fabriken
Mirage sieht vor, dass Sie Fixtures neben Factories verwenden können. Sie können dies erreichen, indem server.loadFixtures()
aufrufen. Zum Beispiel:
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") },
Fixture-Dateien
Idealerweise möchten Sie Ihre Fixtures in einer separaten Datei von server.js
und importieren. Zum Beispiel können Sie ein Verzeichnis namens fixtures
erstellen und darin products.js
erstellen. Fügen Sie in products.js
hinzu:
// <PROJECT-ROOT>/fixtures/products.js export default [ { id: 1, name: 'iPhone 7' }, { id: 2, name: 'Smart TV' }, { id: 3, name: 'Pressing Iron' }, ];
Importieren Sie dann in server.js
und verwenden Sie die Produkthalterung wie folgt:
import products from './fixtures/products'; fixtures: { products, },
Ich verwende die Abkürzung für ES6-Eigenschaften, um das importierte Produkt-Array der products
-Eigenschaft des Fixtures-Objekts zuzuweisen.
Es ist erwähnenswert, dass Fixtures von Mirage JS während Tests ignoriert werden, es sei denn, Sie sagen es ausdrücklich, indem server.loadFixtures()
verwenden.
Fabriken vs. Vorrichtungen
Meiner Meinung nach sollten Sie auf die Verwendung von Vorrichtungen verzichten, es sei denn, Sie haben einen bestimmten Anwendungsfall, in dem sie besser geeignet sind als Fabriken. Vorrichtungen sind in der Regel ausführlicher, während Fabriken schneller sind und weniger Tastenanschläge erfordern.
Serialisierer
Es ist wichtig, eine erwartete JSON-Nutzlast an das Frontend zurückzugeben, daher serializers .
Ein Serialisierer ist ein Objekt, das dafür verantwortlich ist, ein **Modell** oder eine **Sammlung**, die von Ihren Routenhandlern zurückgegeben werden, in eine JSON-Nutzlast umzuwandeln, die so formatiert ist, wie es Ihre Front-End-App erwartet.
Mirage-Dokumente
Nehmen wir zum Beispiel diesen Route-Handler:
this.get('products/:id', (schema, request) => { return schema.products.find(request.params.id); });
Ein Serializer ist dafür verantwortlich, die Antwort in etwa so umzuwandeln:
{ "product": { "rating": 0, "category": "Baby Products", "price": "$654", "name": "Awesome Product 1", "id": "2" } }
Mirage JS eingebaute Serialisierer
Um mit Mirage JS-Serializern zu arbeiten, müssen Sie auswählen, mit welchem integrierten Serializer Sie beginnen möchten. Diese Entscheidung würde durch den JSON-Typ beeinflusst, den Ihr Back-End schließlich an Ihre Front-End-Anwendung senden würde. Mirage ist in den folgenden Serialisierern enthalten:
-
JSONAPISerializer
Dieser Serialisierer folgt der JSON:API-Spezifikation. -
ActiveModelSerializer
Dieser Serializer soll APIs nachahmen, die Rails-APIs ähneln, die mit dem Gem active_model_serializer erstellt wurden. -
RestSerializer
DerRestSerializer
ist ein „Catch All“-Serializer von Mirage JS für andere gängige APIs.
Serializer-Definition
Um eine Serialisierung zu definieren, importieren Sie den entsprechenden Serializer, z. B. RestSerializer
, aus miragejs
wie folgt:
import { Server, RestSerializer } from "miragejs"
Dann in der Server
:
new Server({ serializers: { application: RestSerializer, }, })
Der RestSerializer
wird standardmäßig von Mirage JS verwendet. Es ist also überflüssig, es explizit festzulegen. Der obige Ausschnitt dient beispielhaften Zwecken.
Sehen wir uns die Ausgabe von JSONAPISerializer
und ActiveModelSerializer
auf demselben Routenhandler an, den wir oben definiert haben
JSONAPISerializer
import { Server, JSONAPISerializer } from "miragejs" new Server({ serializers: { application: JSONAPISerializer, }, })
Die Ausgabe:
{ "data": { "type": "products", "id": "2", "attributes": { "rating": 3, "category": "Electronics", "price": "$1711", "name": "Awesome Product 1" } } }
ActiveModelSerializer
Um den ActiveModelSerializer bei der Arbeit zu sehen, würde ich die Deklaration der category
in der Produktfabrik wie folgt ändern:
productCategory() { let categories = [ 'Electronics', 'Computing', 'Fashion', 'Gaming', 'Baby Products', ]; let randomCategoryIndex = Math.floor( Math.random() * categories.length ); let randomCategory = categories[randomCategoryIndex]; return randomCategory; },
Ich habe lediglich den Namen der Eigenschaft in productCategory
, um zu zeigen, wie der Serializer damit umgehen würde.
Dann definieren wir den ActiveModelSerializer
wie folgt:
import { Server, ActiveModelSerializer } from "miragejs" new Server({ serializers: { application: ActiveModelSerializer, }, })
Der Serializer transformiert das zurückgegebene JSON wie folgt:
{ "rating": 2, "product_category": "Computing", "price": "$64", "name": "Awesome Product 4", "id": "5" }
Sie werden feststellen, dass productCategory
in product_category
umgewandelt wurde, was dem Active_model_serializer-Gem des Ruby-Ökosystems entspricht.
Anpassen von Serialisierern
Mirage bietet die Möglichkeit, einen Serializer anzupassen. Angenommen, Ihre Anwendung erfordert, dass Ihre Attributnamen mit Camelcase versehen werden. Sie können RestSerializer
überschreiben, um dies zu erreichen. Wir würden die lodash
Dienstprogrammbibliothek verwenden:
import { RestSerializer } from 'miragejs'; import { camelCase, upperFirst } from 'lodash'; serializers: { application: RestSerializer.extend({ keyForAttribute(attr) { return upperFirst(camelCase(attr)); }, }), },
Dies sollte JSON der Form erzeugen:
{ "Rating": 5, "ProductCategory": "Fashion", "Price": "$1386", "Name": "Awesome Product 4", "Id": "5" }
Einpacken
Sie haben es geschafft! Hoffentlich haben Sie durch diesen Artikel ein tieferes Verständnis von Mirage bekommen und Sie haben auch gesehen, wie Sie durch die Verwendung von Fabriken, Fixtures und Serializern produktionsähnlichere API-Mocks mit Mirage erstellen können.
- Teil 1: Verständnis von Mirage JS-Modellen und -Verknüpfungen
- Teil 2: Factories, Fixtures und Serializer verstehen
- Teil 3: Timing, Reaktion und Passthrough verstehen
- Teil 4: Verwenden von Mirage JS und Cypress für UI-Tests