Mirage JS Deep Dive: Factorys, Fixtures und Serializer verstehen (Teil 2)

Veröffentlicht: 2022-03-10
Kurze Zusammenfassung ↬ In diesem zweiten Teil der Mirage JS Deep Dive-Serie werden wir uns die Fabriken, Vorrichtungen und Serialisierer von Mirage JS ansehen. Wir werden sehen, wie sie mit Mirage ein schnelles API-Mocking ermöglichen.

Im 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') .

Mehr nach dem Sprung! Lesen Sie unten weiter ↓

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
    Der RestSerializer 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