إعداد API Mocking باستخدام Mirage JS و Vue.js
نشرت: 2022-03-10في عصر SPA و JAMstack ، كان هناك دائمًا فصل في الاهتمام بين واجهات برمجة التطبيقات وتطوير الواجهة الأمامية. تتفاعل جميع مشاريع JavaScript تقريبًا التي يمكن اكتشافها في البرية مع خدمة الويب أو واجهة برمجة التطبيقات (API) وتستخدمها إما للمصادقة أو الحصول على البيانات المتعلقة بالمستخدم.
لذلك ، كلما كنت تعمل في مشروع ولم يتم تنفيذ واجهة برمجة التطبيقات الضرورية من قبل فريق الخلفية أو كنت بحاجة إلى اختبار ميزة بسرعة ، فلديك بعض الخيارات التالية:
- يمكنك التوكيل إلى إصدار قيد التشغيل محليًا من الواجهة الخلفية الفعلية الخاصة بك والتي ، في معظم الحالات كمطور للواجهة الأمامية ، لن يكون لديك.
- يمكنك التعليق على الطلب الفعلي واستبداله ببيانات وهمية. (هذا جيد ولكنه ليس رائعًا حيث ستحتاج إلى التراجع عن ذلك للوصول إلى الإنتاج وقد لا تتمكن من التعامل مع حالات الشبكة ووقت الاستجابة.)
ما هو API Mocking؟
الاستهزاء بواجهة برمجة التطبيقات (API) هو تقليد أو محاكاة لواجهة برمجة تطبيقات فعلية. يتم إجراؤه في الغالب لاعتراض الطلبات التي من المفترض أن يتم إجراؤها على واجهة برمجة تطبيقات خلفية فعلية ولكن هذه السخرية موجودة في الواجهة الأمامية.
ما سر أهمية واجهة برمجة التطبيقات (API) للسخرية
السخرية من واجهة برمجة التطبيقات مهمة بشكل كبير من نواحٍ عديدة:
- إنه يجعل تجربة التطوير الأمامية جيدة جدًا بحيث لا تعتمد على واجهات برمجة التطبيقات للإنتاج قبل إنشاء الميزات.
- يمكنك مشاركة الواجهة الأمامية بالكامل وستعمل دون الاعتماد على واجهة برمجة تطبيقات خلفية فعلية.
ما هو Mirage JS؟
تم إنشاء Mirage JS منذ 5 سنوات واستخدم إلى حد كبير في مجتمع Ember قبل أن يعلن Sam Selikoff رسميًا عن إطلاقه في 24 يناير 2020 على Twitter.
يحل Mirage JS نقطة الألم لاختبار واجهات برمجة التطبيقات الخلفية دون الاعتماد على واجهات برمجة التطبيقات هذه. يسمح بتجربة تطوير أمامية سلسة من خلال الاستهزاء بواجهات برمجة تطبيقات الإنتاج.
Mirage JS هي مكتبة محاكاة API لأطر Vue.js و React و Angular و Ember
ما الذي يجعل Mirage JS خيارًا أفضل؟
كانت هناك خيارات أخرى للاستهزاء بواجهة برمجة التطبيقات (مثل أجهزة اعتراض Axios وخادم JSON الخاص بـ Typicode وما إلى ذلك) ولكن ما أعتقد أنه مثير جدًا للاهتمام حول Mirage هو أنه لا يعيق عملية التطوير الخاصة بك (كما سترى عندما قمنا بإعداده مع Vue بعد قليل). إنها خفيفة الوزن لكنها قوية.
يأتي مزودًا ببطارية مضمنة خارج الصندوق تسمح لك بتكرار سيناريوهات استهلاك واجهة برمجة التطبيقات للإنتاج الحقيقي مثل محاكاة شبكة بطيئة بخيار التوقيت الخاص بها.
الشروع في العمل مع Mirage JS و Vue.js
الآن بعد أن عرفت ما هو Mirage JS ولماذا هو مهم لسير عمل تطوير الواجهة الأمامية ، فلنلق نظرة على إعداده باستخدام إطار عمل الويب التقدمي: Vue.js.
إنشاء مشروع Vue للمجال الأخضر (التثبيت النظيف)
باستخدام Vue CLI ، أنشئ مشروع Vue.js جديدًا بالانتقال إلى الدليل الذي تريد إنشاء المشروع فيه وتشغيله (في جهازك الطرفي):
vue create miragejs-demo-vue
سيقوم الأمر أعلاه بإعداد مشروع Vue جديد يمكنك الآن تشغيله وتشغيله إما yarn serve
أو npm run serve
cd
# تثبيت Mirage JS
لنقم الآن بتثبيت Mirage JS كاعتماد تطوير في مشروع Vue.js الخاص بنا عن طريق تشغيل الأمر التالي:
yarn add -D miragejs
أو إذا كنت تستخدم NPM ، فقم بتشغيل هذا:
npm install --save-dev miragejs
وهذا كل شيء! تم تثبيت Mirage JS الآن في مشروع Vue.js الخاص بنا.
دعونا نسخر من شيء ما
مع تثبيت Mirage JS ، دعنا نرى كيف نقوم بتكوينه للتحدث إلى Vue واستخلاص واجهة برمجة تطبيقات todos الأساسية (واجهة برمجة تطبيقات تعرض قائمة من todos).
حدد الخادم الخاص بك
للبدء ، نحتاج إلى إنشاء ملف server.js في دليل /src
من مشروع Vue.js الخاص بنا. بعد ذلك يضاف ما يلي:
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 }
شرح الكود
أولاً ، ملف server.js هو كيفية إعداد Mirage JS لإنشاء مثيل جديد لخادمه الوهمي (وهمي) والذي سيعترض جميع استدعاءات واجهة برمجة التطبيقات التي تجريها في تطبيقك والتي تطابق المسارات التي تحددها.
الآن ، أوافق على أن ما ورد أعلاه قد يكون ساحقًا في البداية ، ولكن دعنا نلقي نظرة فاحصة على ما يحدث هنا:
import { Server, Model } from 'miragejs'
من مقتطف الشفرة أعلاه ، نقوم باستيراد Server
Model
من miragejs
.
-
Server
هذه فئة كشفت عنها Mirage لمساعدتنا في إنشاء مثيل جديد لخادم Mirage JS "للعمل" كخادمنا المزيف. -
Model
فئة أخرى عرضتها Mirage للمساعدة في إنشاء نماذج (يحدد نموذج بنية إدخال قاعدة بيانات Mirage JS) مدعومًا من Mirage's ORM.
export function makeServer({ environment = "development" } = {}) {}
ما ورد أعلاه يقوم أساسًا بتصدير وظيفة تسمى makeServer
من src/server.js
. يمكنك أيضًا ملاحظة أننا نمرر معلمة بيئة ونضبط وضع بيئة Mirage على development
(سترى أننا نجتاز بيئة الاختبار لاحقًا في هذه المقالة).
جسد makeServer
الآن نحن نقوم بأمرين في makeServer
. لنلقي نظرة:
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() }) }, }
أولاً ، نقوم بتمرير معلمة environment
التي بدأناها في تعريف الوظيفة.
models: { todo: Model, },
الخيار التالي وهو خيار models
يأخذ كائنًا من النماذج المختلفة التي نريد Mirage أن يسخر منها.
في ما سبق ، نريد ببساطة نموذج المهام الذي نقوم بإنشائه من فئة النموذج.
seeds(server) { server.create("todo", { content: "Learn Mirage JS" }) server.create("todo", { content: "Integrate With Vue.js" }) },
الخيار التالي هو طريقة البذور التي تأخذ معلمة تسمى 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() }) },
أخيرًا ، لدينا طريقة المسارات التي تحدد المسار المتنوع (المسارات هي نقاط نهاية واجهة API الوهمية الخاصة بنا) سوف يسخر Mirage JS. لنلقِ نظرة على جسم الطريقة:
this.namespace = "api"
يقوم هذا السطر بإعداد مساحة الاسم لجميع المسارات مما يعني أنه يمكن الآن الوصول إلى مسار todo من / api / todos.

this.get("/todos", schema => { return schema.todos.all() })
ما سبق يُنشئ مسار get وهو معالج باستخدام طريقة this.get()
. تتوقع طريقة get()
المسار مثل "/ todos" ووظيفة معالج تأخذ schema
كوسيطة. كائن المخطط هو كيفية تفاعلك مع ORM الخاص بـ Mirage والذي يتم تشغيله بواسطة قاعدة بيانات Mirage JS في الذاكرة.
أخيرا:
return schema.todos.all()
نحن نعيد قائمة بجميع مهامنا ، باستخدام كائن المخطط الذي أصبح ممكنًا بواسطة ORM في Mirage.
src / main.js
لذلك انتهينا من إعداد src/server.js
لكن Vue لا تعرف ذلك (على الأقل ليس بعد). لذلك دعونا نستورده في ملف main.js لدينا مثل:
import { makeServer } from "./server"
ثم نسمي وظيفة makeServer
كما يلي:
if (process.env.NODE_ENV === "development") { makeServer() }
ما ورد أعلاه if
الشرطي هو حارس للتأكد من أن السراب يعمل فقط في التنمية.
الإعداد الكامل!
الآن قمنا بإعداد Miragejs مع Vue. دعونا نراه في العمل. في ملف App.vue الخاص بنا ، سنقوم بمسح المحتوى واستبداله بالمقتطف أدناه:
<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>
إذا كنت معتادًا على Vue.js ، فلن يكون ما سبق شيئًا جديدًا ولكن من أجل أن يكون إجماليًا ، ما نقوم به هو تقديم طلب API باستخدام fetch
عند إنشاء مكون App.vue
بنا ، ثم نقوم بتمرير البيانات التي تم إرجاعها إلى مجموعة todos في حالة المكون لدينا. بعد ذلك ، نستخدم v-for لتكرار مصفوفة todos وعرض خاصية المحتوى لكل مهمة.
أين جزء Mirage JS؟
إذا لاحظت ، في مكون App.vue الخاص بنا ، أننا لم نفعل أي شيء خاص بـ Mirage ، فنحن نقوم فقط باستدعاء API كما نفعل عادة. تعد هذه الميزة في Mirage رائعة حقًا لأن DX تحت غطاء المحرك ، فقد تعترض Mirage أي طلبات تتطابق مع أي من المسارات المحددة في src / server.js أثناء التطوير.
هذا مفيد جدًا لأنه لن تكون هناك حاجة إلى أي عمل في الجزء الخاص بك للتبديل إلى خادم إنتاج حقيقي عندما تكون في بيئة إنتاج بشرط أن تتطابق المسارات مع نقاط نهاية API الخاصة بالإنتاج.
لذا أعد تشغيل خادم Vue dev عبر yarn serve
لاختبار Mirage JS.
يجب أن تشاهد قائمة من اثنين من المهام. الشيء الوحيد الذي قد تجده مثيرًا للاهتمام هو أننا لسنا بحاجة إلى تشغيل أمر طرفي لبدء تشغيل Mirage لأنه يزيل هذا الحمل الزائد عن طريق التشغيل كجزء من تطبيق Vue.js الخاص بك.
أدوات اختبار Mirage JS و Vue
إذا كنت تستخدم بالفعل Vue Test-utils في تطبيق Vue الخاص بك ، فستجد أنه من المثير معرفة أن Mirage يمكنها العمل معها بسهولة لمحاكاة طلبات الشبكة. دعونا نرى مثالاً تم إعداده باستخدام تطبيق todos الخاص بنا.
سنستخدم Jest لاختبار الوحدة لدينا. لذلك ، إذا كنت تتابع ، فيمكنك استخدام Vue CLI إلى حد كبير لتثبيت المكون الإضافي @vue/unit-jest
النحو التالي:
vue add @vue/unit-jest
سيقوم ما سبق بتثبيت تبعيات تطوير @vue/cli-plugin-unit-jest
و @vue/test-utils
أثناء إنشاء دليل tests
وملف jest.config.js . سيضيف أيضًا الأمر التالي في قسم scripts
package.json (أنيق جدًا):
"test:unit": "vue-cli-service test:unit"
دعونا نختبر!
سنقوم بتحديث App.vue ليبدو كما يلي:
<!-- 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>
لا شيء ملحمي حقًا يحدث في المقتطف أعلاه ؛ نحن نقوم فقط بالهيكل للسماح باختبار الشبكة الذي سنقوم بتنفيذه من خلال اختبار الوحدة الخاص بنا.
على الرغم من أن Vue CLI قد أضافت بالفعل مجلد /tests
لنا ، إلا أنني أجد أنها تجربة أفضل بكثير عندما يتم وضع اختباراتي بالقرب من المكونات التي تختبرها. لذا قم بإنشاء مجلد /__tests__
في src/
وأنشئ ملف App.spec.js بداخله. (هذا أيضًا هو النهج الموصى به من قبل 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() })
لذلك لإعداد اختبار الوحدة لدينا ، نقوم باستيراد طريقة mount
من @vue/test-utils
، واستيراد خادم Miragejs الذي أنشأناه سابقًا وأخيراً استيراد مكون App.vue
.
بعد ذلك ، نستخدم الدالة beforeEach
دورة حياة لبدء خادم Mirage JS أثناء المرور في بيئة الاختبار. (تذكر أننا نضع البيئة افتراضيًا لتكون development
).
أخيرًا ، نقوم بإغلاق الخادم باستخدام server.shutdown
في طريقة دورة حياة afterEach
.
اختباراتنا
الآن دعنا نفحص اختبارنا (سنعتمد قسم البدء السريع في مستندات Mirage js. لذا سيبدو App.spec.js أخيرًا كما يلي:
// 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() })
ملاحظة : نحن نستخدم المساعد هنا (كما هو محدد في مستندات Mirage JS). إنه يعيد الوعد الذي يسمح لنا بمعرفة متى تكون العناصر التي نختبرها موجودة بالفعل في DOM.
الآن قم بتشغيل yarn test:unit
.
يجب أن تجتاز جميع اختباراتك في هذه المرحلة.
اختبار حالة الخادم المختلفة باستخدام Mirage JS
يمكننا تغيير خادم Mirage JS الخاص بنا لاختبار حالات الخادم المختلفة. دعونا نرى كيف.
// src/__tests__/App.spec.js import { Response } from "miragejs"
أولاً ، نقوم باستيراد فئة Response
من Mirage ، ثم نقوم بإنشاء سيناريو اختبار جديد مثل:
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." ) })
قم بإجراء الاختبار الخاص بك ويجب أن يمر كل شيء.
خاتمة
تهدف هذه المقالة إلى تقديمك إلى Mirage JS وإظهار كيف تعمل على تحسين تجربة تطوير الواجهة الأمامية. لقد رأينا المشكلة التي تم إنشاؤها Mirage JS لحلها (بناء واجهة أمامية جاهزة للإنتاج بدون أي واجهة برمجة تطبيقات خلفية فعلية) وكيفية إعدادها باستخدام Vue.js.
على الرغم من أن هذا المقال خدش ما يمكن أن يفعله Mirage JS ، أعتقد أنه يكفي لتبدأ.
- يمكنك تصفح المستندات وكذلك الانضمام إلى خادم Mirage JS discord.
- الريبو الداعم لهذه المقالة متاح على GitHub.
مراجع
- ميراج دوكس
- ميراج فيو كويكستارت