API-Mocking mit Mirage JS und Vue.js einrichten
Veröffentlicht: 2022-03-10In der Ära von SPA und JAMstack gab es immer eine Trennung zwischen APIs und Front-End-Entwicklung. Nahezu alle in freier Wildbahn zu findenden JavaScript-Projekte interagieren mit einem Webdienst oder einer API und verwenden diese entweder zur Authentifizierung oder zum Abrufen benutzerbezogener Daten.
Wenn Sie also an einem Projekt arbeiten und die erforderliche API noch nicht vom Backend-Team implementiert wurde oder Sie eine Funktion schnell testen müssen, haben Sie einige der folgenden Optionen:
- Sie könnten zu einer lokal ausgeführten Version Ihres eigentlichen Backends wechseln, was Sie als Frontend-Entwickler in den meisten Fällen nicht hätten.
- Sie könnten die tatsächliche Anfrage auskommentieren und durch Scheindaten ersetzen. (Das ist in Ordnung, aber nicht so toll, da Sie das rückgängig machen müssten, um in die Produktion zu gelangen, und Sie möglicherweise nicht in der Lage sind, mit Netzwerkzuständen und Latenz umzugehen.)
Was ist API-Mocking?
API-Mocking ist eine Nachahmung oder Simulation einer tatsächlichen API. Es wird meistens gemacht, um Anfragen abzufangen, die an eine tatsächliche Backend-API gestellt werden sollen, aber diese Verspottung existiert auf Ihrem Frontend.
Warum ist API-Mocking wichtig?
API-Mocking ist in vielerlei Hinsicht von großer Bedeutung:
- Es sorgt für eine sehr gute Front-End-Entwicklungserfahrung, nicht von Produktions-APIs abhängig zu sein, bevor Funktionen entwickelt werden.
- Sie könnten Ihr gesamtes Frontend teilen und es würde funktionieren, ohne von einer tatsächlichen Backend-API abhängig zu sein.
Was ist Mirage JS?
Mirage JS wurde vor 5 Jahren erstellt und in der Ember-Community ziemlich häufig verwendet, bevor Sam Selikoff seine Veröffentlichung am 24. Januar 2020 offiziell auf Twitter ankündigte.
Mirage JS löst den Problempunkt beim Testen von Backend-APIs, ohne von diesen APIs abhängig zu sein. Es ermöglicht ein nahtloses Front-End-Entwicklungserlebnis, indem Produktions-APIs verspottet werden.
Mirage JS ist eine API-Mocking-Bibliothek für Vue.js-, React-, Angular- und Ember-Frameworks
Was macht Mirage JS zur besseren Wahl?
Es gab andere Optionen für das API-Mocking (wie Axios Interceptors, Typicodes JSON-Server usw.), aber was ich an Mirage ziemlich interessant finde, ist, dass es Ihrem Entwicklungsprozess nicht im Wege steht (wie Sie sehen würden). wenn wir es gleich mit Vue einrichten). Es ist leicht und dennoch leistungsstark.
Im Lieferumfang ist ein Akku enthalten, mit dem Sie reale Produktions-API-Verbrauchsszenarien nachbilden können, z. B. die Simulation eines langsamen Netzwerks mit seiner Timing-Option.
Erste Schritte mit Mirage JS und Vue.js
Nachdem Sie nun wissen, was Mirage JS ist und warum es für Ihren Front-End-Entwicklungs-Workflow wichtig ist, schauen wir uns an, wie es mit dem progressiven Web-Framework Vue.js eingerichtet wird.
Erstellen eines Green-Field (saubere Installation) Vue-Projekts
Erstellen Sie mit der Vue-CLI ein neues Vue.js- Projekt, indem Sie in das Verzeichnis gehen, in dem das Projekt erstellt und ausgeführt werden soll (in Ihrem Terminal):
vue create miragejs-demo-vue
Der obige Befehl würde ein neues Vue-Projekt einrichten, in das Sie nun cd
hinein und entweder yarn serve
' oder npm run serve
.
#Installieren von Mirage JS
Lassen Sie uns nun Mirage JS als Entwicklungsabhängigkeit in unserem Vue.js -Projekt installieren, indem Sie den folgenden Befehl ausführen:
yarn add -D miragejs
Oder wenn Sie NPM verwenden, führen Sie Folgendes aus:
npm install --save-dev miragejs
Und das ist es! Mirage JS ist jetzt in unserem Vue.js- Projekt installiert.
Lass uns etwas verspotten
Wenn Mirage JS installiert ist, sehen wir uns an, wie wir es konfigurieren, um mit Vue zu kommunizieren und eine einfache Todos-API (eine API, die eine Liste von Todos zurückgibt) zu simulieren.
Definieren Sie Ihren Server
Um zu beginnen, müssen wir eine server.js -Datei im /src
-Verzeichnis unseres Vue.js -Projekts erstellen. Fügen Sie danach Folgendes hinzu:
import { Server, Model } from 'miragejs' export function makeServer({ environment = "development" } = {}) { let server = new Server({ environment, models: { todo: Model, }, seeds(server) { server.create("todo", { content: "Learn Mirage JS" }) server.create("todo", { content: "Integrate With Vue.js" }) }, routes() { this.namespace = "api" this.get("/todos", schema => { return schema.todos.all() }) }, }) return server }
Code erklärt
Erstens richten Sie mit der Datei server.js Mirage JS so ein, dass eine neue Instanz seines Scheinservers (Fake-Server) erstellt wird, der alle API-Aufrufe abfängt, die Sie in Ihrer App tätigen und die mit den von Ihnen definierten Routen übereinstimmen.
Nun, ich stimme zu, dass das Obige zunächst überwältigend sein mag, aber lasst uns einen genaueren Blick darauf werfen, was hier vor sich geht:
import { Server, Model } from 'miragejs'
Aus dem obigen Codeausschnitt importieren wir Server
und Model
aus miragejs
.
-
Server
Dies ist eine Klasse, die von Mirage verfügbar gemacht wird, um uns dabei zu helfen, eine neue Instanz eines Mirage JS-Servers zu instanziieren, der als unser gefälschter Server „dienen“ soll. -
Model
Eine weitere Klasse, die von Mirage bereitgestellt wird, um beim Erstellen von Modellen zu helfen (ein Modell bestimmt die Struktur eines Mirage JS-Datenbankeintrags), die von Mirages ORM unterstützt wird.
export function makeServer({ environment = "development" } = {}) {}
Das obige exportiert im Grunde eine Funktion namens makeServer
aus src/server.js
. Sie können auch beachten, dass wir einen Umgebungsparameter übergeben und den Umgebungsmodus von Mirage auf development
setzen (Sie werden später in diesem Artikel sehen, wie wir die Testumgebung bestehen).
Der Körper von makeServer
Jetzt machen wir ein paar Dinge im makeServer
Body. Lass uns einen Blick darauf werfen:
let server = new Server({})
Wir instanziieren eine neue Instanz der Server-Klasse und übergeben ihr eine Konfigurationsoption. Der Inhalt der Konfigurationsoptionen hilft beim Einrichten von Mirage:
{ environment, models: { todo: Model, }, seeds(server) { server.create("todo", { content: "Learn Mirage JS" }) server.create("todo", { content: "Integrate With Vue.js" }) }, routes() { this.namespace = "api" this.get("/todos", schema => { return schema.todos.all() }) }, }
Zuerst übergeben wir den environment
, den wir in der Funktionsdefinition initialisiert haben.
models: { todo: Model, },
Die nächste Option, die Modelloption, nimmt ein Objekt der verschiedenen models
, die Mirage verspotten soll.
Oben wollen wir einfach ein ToDo-Modell, das wir aus der Model-Klasse instanziieren.
seeds(server) { server.create("todo", { content: "Learn Mirage JS" }) server.create("todo", { content: "Integrate With Vue.js" }) },
Die nächste Option ist die Seeds-Methode, die einen Parameter namens server
übernimmt. Die Seeds-Methode hilft beim Erstellen von Seeds (Seeds sind Anfangsdaten oder ein Eintrag in die Datenbank von Mirage) für unsere Modelle. In unserem Fall tun wir Folgendes, um die Samen für das Todo-Modell zu erstellen:
server.create("todo", { content: "Learn Mirage JS" }) server.create("todo", { content: "Integrate With Vue.js" })
Der Server hat also eine create-Methode, die als erstes Argument eine Zeichenfolge erwartet, die dem Namen des Modells entspricht, und dann ein Objekt, das die Eigenschaften oder Attribute eines bestimmten Seeds enthält.
routes() { this.namespace = "api" this.get("/todos", schema => { return schema.todos.all() }) },
Schließlich haben wir die Routes-Methode, die die verschiedenen Routen definiert (Routen sind unsere Schein-API-Endpunkte), die Mirage JS verspotten wird. Schauen wir uns den Hauptteil der Methode an:
this.namespace = "api"
Diese Zeile richtet den Namensraum für alle Routen ein, was bedeutet, dass auf unsere Todo-Route jetzt von /api/todos aus zugegriffen werden kann.
this.get("/todos", schema => { return schema.todos.all() })
Das obige erstellt eine Get-Route und ihren Handler mit der Methode this.get()
. Die Methode get()
erwartet die Route, dh „/todos“ und eine Handler-Funktion, die das schema
als Argument entgegennimmt. Über das Schemaobjekt interagieren Sie mit dem ORM von Mirage, das von der In-Memory-Datenbank von Mirage JS unterstützt wird.
Endlich:
return schema.todos.all()
Wir geben eine Liste aller unserer Todos zurück, indem wir das Schemaobjekt verwenden, das durch Mirage's ORM ermöglicht wird.
src/main.js
Wir sind also mit der Einrichtung von src/server.js
, aber Vue weiß nichts davon (zumindest noch nicht). Importieren wir es also folgendermaßen in unsere main.js -Datei:
import { makeServer } from "./server"
Dann rufen wir die Funktion makeServer
wie folgt auf:
if (process.env.NODE_ENV === "development") { makeServer() }
Die obige if
-Bedingung ist ein Wächter, um sicherzustellen, dass Mirage nur in der Entwicklung läuft.
Fertig eingerichtet!
Jetzt haben wir Miragejs mit Vue eingerichtet. Sehen wir es uns in Aktion an. In unserer App.vue -Datei würden wir den Inhalt löschen und durch das folgende Snippet ersetzen:
<template> <ul> <li v-for="todo in todos" v-bind:key="todo.id">{{ todo.content }}</li> </ul> </template> <script> export default { name: 'app', data() { return { todos: [] } }, created() { fetch("/api/todos") .then(res => res.json()) .then(json => { this.todos = json.todos }) } } </script>
Wenn Sie mit Vue.js vertraut sind, wäre das Obige nichts Neues, aber der Vollständigkeit halber machen wir eine API-Anfrage mit fetch
, wenn unsere App.vue
Komponente erstellt wird, dann übergeben wir die zurückgegebenen Daten zum Todos-Array in unserem Komponentenzustand. Anschließend verwenden wir ein v-for, um das Todos-Array zu durchlaufen und die Inhaltseigenschaft jeder Aufgabe anzuzeigen.
Wo ist der Mirage JS-Teil?
Wenn Sie bemerken, dass wir in unserer App.vue-Komponente nichts speziell für Mirage getan haben, sondern nur einen API-Aufruf durchführen, wie wir es normalerweise tun würden. Diese Funktion von Mirage ist wirklich großartig für DX, denn unter der Haube würde Mirage alle Anfragen abfangen, die mit einer der in src/server.js definierten Routen übereinstimmen, während Sie sich in der Entwicklung befinden.
Dies ist ziemlich praktisch, da Ihrerseits keine Arbeit erforderlich wäre, um zu einem tatsächlichen Produktionsserver zu wechseln, wenn Sie sich in einer Produktionsumgebung befinden, vorausgesetzt, die Routen stimmen mit Ihren Produktions-API-Endpunkten überein.
Starten Sie also Ihren Vue-Entwicklungsserver über yarn serve
neu, um Mirage JS zu testen.
Sie sollten eine Liste mit zwei Todos sehen. Eine Sache, die Sie ziemlich interessant finden werden, ist, dass wir keinen Terminalbefehl ausführen mussten, um Mirage zu starten, da es diesen Overhead beseitigt, indem es als Teil Ihrer Vue.js-Anwendung ausgeführt wird.
Mirage JS und Vue Test-Dienstprogramme
Wenn Sie bereits Vue Test-utils in Ihrer Vue-Anwendung verwenden, werden Sie es spannend finden zu wissen, dass Mirage problemlos damit arbeiten kann, um Netzwerkanfragen zu simulieren. Sehen wir uns ein Beispiel an, das mit unserer Todos-Anwendung eingerichtet wurde.
Wir würden Jest für unsere Komponententests verwenden. Wenn Sie also mitmachen, können Sie die Vue-CLI verwenden, um das Plugin @vue/unit-jest
so zu installieren:
vue add @vue/unit-jest
Das Obige installiert die Entwicklungsabhängigkeiten @vue/cli-plugin-unit-jest
und @vue/test-utils
und erstellt gleichzeitig ein Verzeichnis tests
und eine Datei jest.config.js . Es wird auch den folgenden Befehl in unserem Paket.json-Skriptabschnitt scripts
(ziemlich ordentlich):
"test:unit": "vue-cli-service test:unit"
Lassen Sie uns testen!
Wir würden unsere App.vue so aktualisieren, dass sie so aussieht:
<!-- src/App.vue --> <template> <div v-if="serverError" data-test> {{ serverError }} </div> <div v-else-if="todos.length === 0" data-test> No todos! </div> <div v-else> <ul> <li v-for="todo in todos" v-bind:key="todo.id" :data-test > {{ todo.content }} </li> </ul> </div> </template> <script> export default { name: "app", data() { return { todos: [], serverError: null, } }, created() { fetch("/api/todos") .then(res => res.json()) .then(json => { if (json.error) { this.serverError = json.error } else { this.todos = json.todos } }) }, } </script>
Im obigen Ausschnitt passiert nichts wirklich Episches; Wir strukturieren nur, um die Netzwerktests zu ermöglichen, die wir mit unserem Komponententest implementieren würden.
Obwohl Vue CLI bereits einen /tests
-Ordner für uns hinzugefügt hat, finde ich es viel besser, wenn meine Tests in der Nähe der Komponenten platziert werden, die sie testen. Erstellen Sie also einen /__tests__
Ordner in src/
und erstellen Sie darin eine App.spec.js -Datei. (Dies ist auch der von Jest empfohlene Ansatz.)
// src/__tests__/App.spec.js import { mount } from "@vue/test-utils" import { makeServer } from "../server" import App from "../App.vue" let server beforeEach(() => { server = makeServer({ environment: "test" }) }) afterEach(() => { server.shutdown() })
Um also unsere Komponententests einzurichten, importieren wir die mount
-Methode von @vue/test-utils
, importieren den Miragejs-Server, den wir zuvor erstellt haben, und importieren schließlich die App.vue
Komponente.
Als Nächstes verwenden wir die beforeEach
-Lebenszyklusfunktion, um den Mirage JS-Server zu starten, während wir die Testumgebung übergeben. (Denken Sie daran, dass wir die Umgebung standardmäßig auf development
festgelegt haben.)
Zuletzt fahren wir den Server mit server.shutdown
in der afterEach
Lebenszyklusmethode herunter.
Unsere Tests
Lassen Sie uns nun unseren Test konkretisieren (wir würden den Quickstart-Abschnitt der Mirage js-Dokumentation übernehmen. Ihre App.spec.js würde also letztendlich so aussehen:
// src/__tests__/App.spec.js import { mount } from "@vue/test-utils" import { makeServer } from "./server" import App from "./App.vue" let server beforeEach(() => { server = makeServer({ environment: "test" }) }) it("shows the todos from our server", async () => { server.create("todo", { id: 1, content: "Learn Mirage JS" }) server.create("todo", { id: 2, content: "Integrate with Vue.js" }) const wrapper = mount(App) // let's wait for our vue component to finish loading data // we know it's done when the data-testid enters the dom. await waitFor(wrapper, '[data-test]') await waitFor(wrapper, '[data-test]') expect(wrapper.find('[data-test]').text()).toBe("Learn Mirage JS") expect(wrapper.find('[data-test]').text()).toBe("Integrate with Vue.js") }) it("shows a message if there are no todo", async () => { // Don't create any todos const wrapper = mount(App) await waitFor(wrapper, '[data-test]') expect(wrapper.find('[data-test]').text()).toBe("No todos!") }) // This helper method returns a promise that resolves // once the selector enters the wrapper's dom. const waitFor = function(wrapper, selector) { return new Promise(resolve => { const timer = setInterval(() => { const todoEl = wrapper.findAll(selector) if (todoEl.length > 0) { clearInterval(timer) resolve() } }, 100) }) } afterEach(() => { server.shutdown() })
Hinweis : Wir verwenden hier einen Helfer (wie in der Mirage JS-Dokumentation definiert). Es gibt ein Versprechen zurück, das es uns ermöglicht zu wissen, wann die Elemente, die wir testen, bereits im DOM sind.
Führen Sie nun yarn test:unit
aus.
Alle Ihre Tests sollten zu diesem Zeitpunkt bestanden werden.
Testen unterschiedlicher Serverzustände mit Mirage JS
Wir könnten unseren Mirage JS-Server ändern, um verschiedene Serverzustände zu testen. Mal sehen wie.
// src/__tests__/App.spec.js import { Response } from "miragejs"
Zuerst importieren wir die Response
-Klasse von Mirage, dann erstellen wir ein neues Testszenario wie folgt:
it("handles error responses from the server", async () => { // Override Mirage's route handler for /todos, just for this test server.get("/todos", () => { return new Response( 500, {}, { error: "The database is taking a break.", } ) }) const wrapper = mount(App) await waitFor(wrapper, '[data-test]') expect(wrapper.find('[data-test]').text()).toBe( "The database is taking a break." ) })
Führen Sie Ihren Test durch und alles sollte bestehen.
Fazit
Dieser Artikel soll Ihnen Mirage JS vorstellen und Ihnen zeigen, wie es zu einer besseren Front-End-Entwicklungserfahrung beiträgt. Wir haben das Problem gesehen, das Mirage JS geschaffen hat, um es anzugehen (Erstellen eines produktionsbereiten Front-Ends ohne eine tatsächliche Back-End-API) und wie man es mit Vue.js einrichtet.
Obwohl dieser Artikel nur an der Oberfläche dessen kratzt, was Mirage JS kann, glaube ich, dass er für den Einstieg ausreicht.
- Sie können die Dokumente durchgehen und dem Mirage JS-Discord-Server beitreten.
- Das unterstützende Repository für diesen Artikel ist auf GitHub verfügbar.
Verweise
- Die Mirage-Dokumentation
- Mirage Vue-Schnellstart