Konfigurowanie szyderstwa API za pomocą Mirage JS i Vue.js
Opublikowany: 2022-03-10W erze SPA i JAMstack zawsze istniał podział troski między API a rozwojem front-endu. Prawie wszystkie projekty JavaScript, które można znaleźć na wolności, wchodzą w interakcję z usługą internetową lub interfejsem API i używają ich do uwierzytelniania lub uzyskiwania danych związanych z użytkownikiem.
Tak więc za każdym razem, gdy pracujesz nad projektem, a niezbędny interfejs API nadal nie został zaimplementowany przez zespół back-end lub potrzebujesz szybko przetestować funkcję, masz do wyboru niektóre z następujących opcji:
- Możesz proxy do lokalnie działającej wersji twojego rzeczywistego zaplecza, której w większości przypadków jako programista frontonu nie miałbyś.
- Możesz skomentować rzeczywiste żądanie i zastąpić je próbnymi danymi. (Jest to w porządku, ale nie tak wspaniałe, jak trzeba by to cofnąć, aby dostać się do środowiska produkcyjnego, a może nie być w stanie poradzić sobie ze stanami sieci i opóźnieniami).
Co to jest szydzenie z API?
Mockowanie API to imitacja lub symulacja rzeczywistego API. Odbywa się to głównie w celu przechwycenia żądań, które mają być kierowane do rzeczywistego interfejsu API zaplecza, ale takie prześmiewanie istnieje w twoim interfejsie.
Dlaczego mockowanie API jest ważne
Mockowanie API jest bardzo ważne na wiele sposobów:
- To sprawia, że bardzo dobre środowisko programowania front-end nie zależy od produkcyjnych interfejsów API przed tworzeniem funkcji.
- Możesz udostępnić cały frontend i działałoby to bez zależności od rzeczywistego interfejsu API zaplecza.
Co to jest Mirage JS?
Mirage JS powstał 5 lat temu i był w dużej mierze używany w społeczności Ember, zanim Sam Selikoff oficjalnie ogłosił swoją premierę 24 stycznia 2020 r. na Twitterze.
Mirage JS rozwiązuje problem testowania interfejsów API zaplecza bez polegania na tych interfejsach API. Pozwala na bezproblemowe tworzenie front-endu poprzez mocowanie produkcyjnych interfejsów API.
Mirage JS to biblioteka mocking API dla frameworków Vue.js, React, Angular i Ember
Co sprawia, że Mirage JS jest lepszym wyborem?
Istniały inne opcje podszycia API (takie jak przechwytywacze Axios, serwer JSON Typicode i tak dalej), ale myślę, że w Mirage jest całkiem interesujące to, że nie przeszkadza to w procesie rozwoju (jak widać kiedy skonfigurujemy to z Vue za chwilę). Jest lekki, a jednocześnie mocny.
Jest dostarczany z baterią dostarczoną po wyjęciu z pudełka, która umożliwia replikowanie rzeczywistych scenariuszy wykorzystania interfejsu API w środowisku produkcyjnym, takich jak symulacja wolnej sieci z opcją pomiaru czasu.
Pierwsze kroki z Mirage JS i Vue.js
Teraz, gdy już wiesz, czym jest Mirage JS i dlaczego jest ważny dla przepływu pracy programistycznej, przyjrzyjmy się skonfigurowaniu go za pomocą progresywnego frameworka internetowego: Vue.js.
Tworzenie projektu „Green-Field” (czysta instalacja) Vue
Używając Vue CLI, stwórz nowy projekt Vue.js , przechodząc do katalogu, w którym chcesz, aby projekt został utworzony i uruchomiony (w twoim terminalu):
vue create miragejs-demo-vue
Powyższe polecenie utworzy nowy projekt Vue, do którego możesz teraz cd
i uruchomić albo yarn serve
lub npm run serve
.
#Instalowanie Mirage JS
Teraz zainstalujmy Mirage JS jako zależność programistyczną w naszym projekcie Vue.js , uruchamiając następujące polecenie:
yarn add -D miragejs
Lub jeśli używasz NPM, uruchom to:
npm install --save-dev miragejs
I to wszystko! Mirage JS jest teraz zainstalowany w naszym projekcie Vue.js.
Wyśmiejmy się z czegoś
Po zainstalowaniu Mirage JS zobaczmy, jak skonfigurujemy go do komunikacji z Vue i wykpiwania podstawowego API zadań do wykonania (interfejsu API, który zwraca listę zadań).
Zdefiniuj swój serwer
Aby rozpocząć, musimy utworzyć plik server.js w katalogu /src
naszego projektu Vue.js. Następnie dodaj następujące:
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 }
Wyjaśnienie kodu
Po pierwsze, plik server.js służy do konfigurowania Mirage JS, aby utworzyć nową instancję jego pozorowanego (fałszywego) serwera, który przechwyci wszystkie wywołania API, które wykonujesz w swojej aplikacji, pasujące do zdefiniowanych przez Ciebie tras.
Teraz zgadzam się, że powyższe może być na początku przytłaczające, ale przyjrzyjmy się bliżej temu, co się tutaj dzieje:
import { Server, Model } from 'miragejs'
Z powyższego fragmentu kodu importujemy Server
i Model
z miragejs
.
-
Server
Jest to klasa ujawniona przez Mirage, która pomaga nam utworzyć instancję nowej instancji serwera Mirage JS, aby „służyć” jako nasz fałszywy serwer. -
Model
Kolejna klasa udostępniona przez Mirage, aby pomóc w tworzeniu modeli (model określa strukturę wpisu bazy danych Mirage JS) obsługiwana przez ORM Mirage.
export function makeServer({ environment = "development" } = {}) {}
Powyższe w zasadzie eksportuje funkcję o nazwie makeServer
z src/server.js
. Możesz również zauważyć, że przekazujemy parametr środowiska i ustawiamy tryb środowiska Mirage na development
(możesz zobaczyć, jak przekazujemy środowisko testowe w dalszej części tego artykułu).
Ciało makeServer
Teraz robimy kilka rzeczy w ciele makeServer
. Spójrzmy:
let server = new Server({})
Tworzymy nową instancję klasy Server i przekazujemy jej opcję konfiguracyjną. Zawartość opcji konfiguracyjnych pomaga skonfigurować miraż:
{ 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() }) }, }
Najpierw przekazujemy parametr environment
, który zainicjowaliśmy w definicji funkcji.
models: { todo: Model, },
Następna opcja, czyli models
, przyjmuje obiekt z różnych modeli, z których Mirage ma się wyśmiewać.
W powyższym kodzie po prostu chcemy mieć model rzeczy do zrobienia, który tworzymy z klasy Model.
seeds(server) { server.create("todo", { content: "Learn Mirage JS" }) server.create("todo", { content: "Integrate With Vue.js" }) },
Następną opcją jest metoda seedów, która przyjmuje parametr o nazwie server
. Metoda nasion pomaga tworzyć nasiona (nasiona to dane początkowe lub wpis do bazy danych Mirage) dla naszych modeli. W naszym przypadku, aby stworzyć nasiona dla modelu todo, wykonujemy:
server.create("todo", { content: "Learn Mirage JS" }) server.create("todo", { content: "Integrate With Vue.js" })
serwer ma więc metodę create, która jako pierwszy argument oczekuje napisu odpowiadającego nazwie modelu, a następnie obiektu, który będzie zawierał właściwości lub atrybuty konkretnego seeda.
routes() { this.namespace = "api" this.get("/todos", schema => { return schema.todos.all() }) },
na koniec mamy metodę route, która definiuje różne trasy (trasy są naszymi mockowymi punktami końcowymi API) Mirage JS zamierza zakpić. Spójrzmy na treść metody:
this.namespace = "api"
ta linia ustawia przestrzeń nazw dla wszystkich tras, co oznacza, że nasza trasa todo może być teraz dostępna z /api/todos.
this.get("/todos", schema => { return schema.todos.all() })
Powyższe tworzy trasę get i jej obsługę przy użyciu metody this.get()
. Metoda get()
oczekuje trasy, tj. „/todos” i funkcji obsługi, która jako argument przyjmuje schema
. Obiekt schematu to sposób interakcji z ORM Mirage, który jest zasilany przez bazę danych Mirage JS w pamięci.
Wreszcie:
return schema.todos.all()
Zwracamy listę wszystkich naszych rzeczy do zrobienia, używając obiektu schema, który jest możliwy dzięki ORM-owi Mirage.
src/main.js
Skończyliśmy więc konfigurować src/server.js
, ale Vue o tym nie wie (przynajmniej jeszcze nie). Zaimportujmy go więc do naszego pliku main.js w następujący sposób:
import { makeServer } from "./server"
Następnie wywołujemy funkcję makeServer
w następujący sposób:
if (process.env.NODE_ENV === "development") { makeServer() }
Powyższe warunkowe if
strażnikiem, aby upewnić się, że miraż działa tylko w fazie rozwoju.
Konfiguracja zakończona!
Teraz skonfigurowaliśmy Miragejs z Vue. Zobaczmy to w akcji. W naszym pliku App.vue wyczyścilibyśmy zawartość i zamienilibyśmy ją na poniższy fragment:
<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>
Jeśli znasz Vue.js, powyższe nie byłoby niczym nowym, ale ze względu na całość, robimy żądanie API za pomocą funkcji fetch
, gdy tworzony jest nasz komponent App.vue
, a następnie przekazujemy zwrócone dane do tablicy rzeczy do zrobienia w naszym stanie komponentu. Następnie używamy v-for do iteracji tablicy zadań do wykonania i wyświetlania właściwości content każdego zadania.
Gdzie jest część Mirage JS?
Jeśli zauważysz, że w naszym komponencie App.vue nie zrobiliśmy nic konkretnego dla Mirage, po prostu wykonujemy wywołanie API, tak jak zwykle. Ta funkcja Mirage jest naprawdę świetna dla DX-a, ponieważ pod maską Mirage przechwytuje wszystkie żądania, które pasują do dowolnej trasy zdefiniowanej w src/server.js, gdy jesteś w fazie rozwoju.
Jest to bardzo przydatne, ponieważ przejście do rzeczywistego serwera produkcyjnego w środowisku produkcyjnym nie wymagałoby żadnej pracy, pod warunkiem, że trasy są zgodne z punktami końcowymi interfejsu produkcyjnego interfejsu API.
Zrestartuj więc serwer deweloperski Vue za pomocą usługi yarn serve
, aby przetestować Mirage JS.
Powinieneś zobaczyć listę dwóch rzeczy do zrobienia. Jedną z rzeczy, które można uznać za całkiem interesujące, jest to, że nie musieliśmy uruchamiać polecenia terminala, aby uruchomić Mirage, ponieważ usuwa to obciążenie, uruchamiając jako część aplikacji Vue.js.
Mirage JS i narzędzia testowe Vue
Jeśli już korzystasz z narzędzi Vue Test-utils w swojej aplikacji Vue, ekscytująca będzie dla Ciebie świadomość, że Mirage może z łatwością pracować z nią w celu symulowania żądań sieciowych. Zobaczmy przykład skonfigurowany przy użyciu naszej aplikacji Todos.
Używalibyśmy Jest do naszych testów jednostkowych. Więc jeśli podążasz dalej, możesz użyć Vue CLI do zainstalowania wtyczki @vue/unit-jest
następujący sposób:
vue add @vue/unit-jest
Powyższe zainstaluje zależności programistyczne @vue/cli-plugin-unit-jest
i @vue/test-utils
, jednocześnie tworząc katalog tests
i plik jest.config.js . Doda również następującą komendę w naszej sekcji scripts
package.json (całkiem zgrabna):
"test:unit": "vue-cli-service test:unit"
Przetestujmy!
Zaktualizowalibyśmy nasz App.vue , aby wyglądał tak:
<!-- 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>
W powyższym fragmencie nie dzieje się nic naprawdę epickiego; po prostu budujemy strukturę, aby umożliwić testowanie sieci, które wdrażalibyśmy za pomocą naszego testu jednostkowego.
Chociaż Vue CLI już dodał dla nas folder /tests
, uważam, że jest to znacznie lepsze doświadczenie, gdy moje testy są umieszczone blisko testowanych komponentów. Stwórz więc folder /__tests__
w src/
i utwórz w nim plik App.spec.js. (Jest to również zalecane podejście przez Jest.)
// 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() })
Aby skonfigurować nasze testy jednostkowe, importujemy metodę mount
z @vue/test-utils
, importujemy serwer Miragejs, który utworzyliśmy wcześniej, a na koniec importujemy komponent App.vue
.
Następnie używamy funkcji cyklu życia beforeEach
, aby uruchomić serwer Mirage JS podczas przechodzenia w środowisku testowym. (Pamiętaj, że domyślnie ustawiamy środowisko development
.)
Na koniec zamykamy serwer za pomocą server.shutdown
w metodzie cyklu życia afterEach
.
Nasze testy
Teraz rozwińmy nasz test (zaadoptowalibyśmy sekcję szybkiego startu w dokumentacji Mirage js. Więc twój App.spec.js w końcu wyglądałby tak:
// 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() })
Uwaga : używamy tutaj pomocnika (zgodnie z definicją w dokumentacji Mirage JS). Zwraca obietnicę, która pozwala nam wiedzieć, kiedy elementy, które testujemy, są już w DOM.
Teraz uruchom yarn test:unit
.
Wszystkie twoje testy powinny zaliczyć w tym momencie.
Testowanie różnych stanów serwera za pomocą Mirage JS
Możemy zmienić nasz serwer Mirage JS, aby przetestować różne stany serwera. Zobaczmy jak.
// src/__tests__/App.spec.js import { Response } from "miragejs"
Najpierw importujemy klasę Response
z Mirage, a następnie tworzymy nowy scenariusz testowy, taki jak:
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." ) })
Uruchom test i wszystko powinno zdać.
Wniosek
Ten artykuł ma na celu wprowadzenie do Mirage JS i pokazanie, w jaki sposób zapewnia lepsze środowisko programistyczne. Widzieliśmy problem, który Mirage JS stworzył, aby rozwiązać (budowanie front-endu gotowego do produkcji bez żadnego faktycznego API zaplecza) i jak skonfigurować go za pomocą Vue.js.
Chociaż ten artykuł zarysował powierzchnię tego, co może zrobić Mirage JS, uważam, że to wystarczy, aby zacząć.
- Możesz przejrzeć dokumenty, a także dołączyć do serwera discord Mirage JS.
- Obsługiwane repozytorium tego artykułu jest dostępne w serwisie GitHub.
Bibliografia
- Dokumenty Mirage
- Mirage Vue — krótkie wprowadzenie