الغوص العميق في Mirage JS: فهم المصانع والتركيبات والمسلسلات (الجزء الثاني)
نشرت: 2022-03-10في المقالة السابقة من هذه السلسلة ، درسنا العارضات والجمعيات من حيث صلتها بـ Mirage. لقد أوضحت أن النماذج تسمح لنا بإنشاء بيانات وهمية ديناميكية يمكن أن تخدمها Mirage لتطبيقنا عندما تقدم طلبًا لنقاط النهاية الوهمية الخاصة بنا. في هذه المقالة ، سنلقي نظرة على ثلاث ميزات أخرى من Mirage تسمح بسخرية أكثر سرعة لواجهة برمجة التطبيقات. دعنا نتعمق في!
ملحوظة : أوصي بشدة بقراءة أول مقالتين لي إذا لم تكن قد حصلت على معالجة قوية لما سيتم مناقشته هنا. ومع ذلك ، لا يزال بإمكانك متابعة المقالات السابقة والرجوع إليها عند الضرورة.
- إعداد API Mocking باستخدام Mirage JS و Vue
- نماذج وجمعيات Mirage JS
المصانع
في مقال سابق ، أوضحت كيفية استخدام Mirage JS للسخرية من واجهة برمجة التطبيقات الخلفية ، والآن لنفترض أننا نسخر من مورد منتج في Mirage. لتحقيق ذلك ، سننشئ معالج مسار يكون مسؤولاً عن اعتراض الطلبات إلى نقطة نهاية معينة ، وفي هذه الحالة ، تكون نقطة النهاية هي api/products
. سيعيد معالج المسار الذي أنشأناه جميع المنتجات. فيما يلي الكود لتحقيق ذلك في ميراج:
import { Server, Model } from 'miragejs'; new Server({ models: { product: Model, }, routes() { this.namespace = "api"; this.get('products', (schema, request) => { return schema.products.all() }) } }); },
ناتج ما سبق سيكون:
{ "products": [] }
نرى من الإخراج أعلاه أن مورد المنتج فارغ. هذا أمر متوقع لأننا لم ننشئ أي سجلات بعد.
نصيحة احترافية: يوفر Mirage الاختزال المطلوب لنقاط نهاية API التقليدية. لذلك يمكن أن يكون معالج المسار أعلاه قصيرًا مثل: this.get('/products')
.
لنقم بإنشاء سجلات لنموذج product
ليتم تخزينها في قاعدة بيانات Mirage باستخدام طريقة seeds
في مثيل Server
الخاص بنا:
seeds(server) { server.create('product', { name: 'Gemini Jacket' }) server.create('product', { name: 'Hansel Jeans' }) },
الإخراج:
{ "products": [ { "name": "Gemini Jacket", "id": "1" }, { "name": "Hansel Jeans", "id": "2" } ] }
كما ترى أعلاه ، عندما يقدم تطبيق الواجهة الأمامية طلبًا إلى /api/products
، فسوف يستعيد مجموعة من المنتجات على النحو المحدد في طريقة seeds
.
يعد استخدام طريقة seeds
لبذر قاعدة بيانات ميراج خطوة من الحاجة إلى إنشاء كل إدخال يدويًا ككائن. ومع ذلك ، لن يكون من العملي إنشاء 1000 (أو مليون) سجل منتج جديد باستخدام النمط أعلاه. ومن هنا جاءت الحاجة إلى المصانع .
شرح المصانع
المصانع هي طريقة أسرع لإنشاء سجلات قاعدة بيانات جديدة. إنها تتيح لنا إنشاء سجلات متعددة بسرعة لنموذج معين مع وجود اختلافات يتم تخزينها في قاعدة بيانات Mirage JS.
المصانع هي أيضًا كائنات تجعل من السهل إنشاء بيانات تبدو واقعية دون الحاجة إلى زرع تلك البيانات بشكل فردي. المصانع هي أكثر من وصفات أو مخططات لإنشاء سجلات من النماذج.
إنشاء مصنع
دعونا نفحص مصنعًا عن طريق إنشاء مصنع. سيتم استخدام المصنع الذي سننشئه كمخطط لإنشاء منتجات جديدة في قاعدة بيانات Mirage JS الخاصة بنا.
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({}) } })
مما سبق ، سترى أننا أضفنا خاصية factories
إلى مثيل Server
الخاص بنا وقمنا بتعريف خاصية أخرى بداخلها والتي من خلال الاتفاقية تحمل نفس اسم النموذج الذي نريد إنشاء مصنع له ، في هذه الحالة ، يكون هذا النموذج هو نموذج product
. المقتطف أعلاه يصور النمط الذي ستتبعه عند إنشاء مصانع في Mirage JS.
على الرغم من أن لدينا مصنعًا لنموذج product
، إلا أننا في الحقيقة لم نقم بإضافة خصائص إليه. يمكن أن تكون خصائص المصنع أنواعًا بسيطة مثل السلاسل أو القيم المنطقية أو الأرقام ، أو الوظائف التي تُرجع البيانات الديناميكية كما سنرى في التنفيذ الكامل لمصنع المنتج الجديد أدناه:
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; }, }), }, })
في مقتطف الشفرة أعلاه ، نحدد بعض منطق جافا سكريبت عبر Math.random
لإنشاء بيانات ديناميكية في كل مرة يتم فيها استخدام المصنع لإنشاء سجل منتج جديد. هذا يدل على قوة ومرونة المصانع.
لنقم بإنشاء منتج باستخدام المصنع الذي حددناه أعلاه. للقيام بذلك ، نقوم باستدعاء server.create
وتمرير اسم النموذج ( product
) كسلسلة. ستقوم ميراج بعد ذلك بإنشاء رقم قياسي جديد لمنتج باستخدام مصنع المنتج الذي حددناه. الكود الذي تحتاجه للقيام بذلك هو ما يلي:
new Server({ seeds(server) { server.create("product") } })
نصيحة للمحترفين : يمكنك تشغيل console.log(server.db.dump())
لرؤية السجلات في قاعدة بيانات ميراج.
تم إنشاء سجل جديد مشابه للسجل أدناه وتخزينه في قاعدة بيانات ميراج.
{ "products": [ { "rating": 3, "category": "Computing", "price": "$739", "name": "Awesome Product 0", "id": "1" } ] }
تجاوز المصانع
يمكننا تجاوز بعض أو أكثر من القيم التي يوفرها المصنع عن طريق تمريرها صراحة على النحو التالي:
server.create("product", {name: "Yet Another Product", rating: 5, category: "Fashion" })
سيكون السجل الناتج مشابهًا لـ:
{ "products": [ { "rating": 5, "category": "Fashion", "price": "$782", "name": "Yet Another Product", "id": "1" } ] }
إنشاء قائمة
مع وجود مصنع في مكانه الصحيح ، يمكننا استخدام طريقة أخرى على كائن الخادم تسمى createList
. تسمح هذه الطريقة بإنشاء سجلات متعددة لنموذج معين عن طريق تمرير اسم النموذج وعدد السجلات التي تريد إنشاءها. أدناه هو استخدامه:
server.createList("product", 10)
أو
server.createList("product", 1000)
كما ستلاحظ ، تأخذ طريقة createList
أعلاه وسيطين: اسم النموذج كسلسلة وعدد صحيح موجب غير صفري يمثل عدد السجلات المراد إنشاؤها. لذا مما سبق ، أنشأنا للتو 500 سجل من المنتجات! هذا النمط مفيد لاختبار واجهة المستخدم كما سترى في مقال مستقبلي من هذه السلسلة.
تركيبات
في اختبار البرنامج ، تعتبر أداة الاختبار أو أداة التثبيت عبارة عن حالة مجموعة أو مجموعة من الكائنات التي تعمل كخط أساس لإجراء الاختبارات. الغرض الرئيسي من الجهاز هو التأكد من أن بيئة الاختبار معروفة جيدًا من أجل جعل النتائج قابلة للتكرار.
يتيح لك Mirage إنشاء تركيبات واستخدامها لبذر قاعدة البيانات الخاصة بك بالبيانات الأولية.
ملحوظة : يوصى باستخدام المصانع 9 مرات من أصل 10 لأنها تجعل من السهل صيانتها.
خلق لاعبا اساسيا
لنقم بإنشاء أداة بسيطة لتحميل البيانات على قاعدة البيانات الخاصة بنا:
fixtures: { products: [ { id: 1, name: 'T-shirts' }, { id: 2, name: 'Work Jeans' }, ], },
يتم تحميل البيانات الواردة أعلاه تلقائيًا في قاعدة البيانات كبيانات أولية لـ Mirage. ومع ذلك ، إذا كانت لديك وظيفة بذور محددة ، فإن Mirage ستتجاهل تركيباتك بالافتراضات التي كنت تقصدها لتجاوزها وتستخدم بدلاً من ذلك المصانع لبذر بياناتك.
تركيبات مقترنة بالمصانع
يوفر لك Mirage إمكانية استخدام التركيبات جنبًا إلى جنب مع المصانع. يمكنك تحقيق ذلك عن طريق استدعاء server.loadFixtures()
. علي سبيل المثال:
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") },
ملفات ثابتة
من الناحية المثالية ، قد ترغب في إنشاء تركيباتك في ملف منفصل من server.js
واستيراده. على سبيل المثال ، يمكنك إنشاء دليل يسمى fixtures
إنشاء products.js
. في products.js
يضاف:
// <PROJECT-ROOT>/fixtures/products.js export default [ { id: 1, name: 'iPhone 7' }, { id: 2, name: 'Smart TV' }, { id: 3, name: 'Pressing Iron' }, ];
ثم في server.js
، استورد واستخدم تركيبات المنتجات مثل:
import products from './fixtures/products'; fixtures: { products, },
أنا أستخدم اختصار الخاصية ES6 لتعيين مجموعة المنتجات المستوردة لخاصية products
الخاصة بكائن التركيبات.
ومن الجدير بالذكر أن شركة Mirage JS ستتجاهل التركيبات أثناء الاختبارات باستثناء أنك تخبرها صراحةً بعدم القيام بذلك باستخدام server.loadFixtures()
المصانع مقابل التركيبات
في رأيي ، يجب الامتناع عن استخدام التركيبات باستثناء حالة استخدام معينة حيث تكون أكثر ملاءمة من المصانع. تميل التركيبات إلى أن تكون أكثر طولًا بينما تكون المصانع أسرع وتنطوي على ضغطات أقل على المفاتيح.
المسلسلات
من المهم إرجاع حمولة JSON المتوقعة إلى الواجهة الأمامية ومن ثم المتسلسلات .
المُسلسل هو كائن مسؤول عن تحويل ** Model ** أو ** Collection ** التي يتم إرجاعها من معالجات المسار إلى حمولة JSON يتم تنسيقها بالطريقة التي يتوقعها تطبيق الواجهة الأمامية.
ميراج دوكس
لنأخذ معالج المسار هذا على سبيل المثال:
this.get('products/:id', (schema, request) => { return schema.products.find(request.params.id); });
المسلسل مسؤول عن تحويل الاستجابة إلى شيء مثل هذا:
{ "product": { "rating": 0, "category": "Baby Products", "price": "$654", "name": "Awesome Product 1", "id": "2" } }
مسلسلات مدمجة من ميراج JS
للعمل مع مسلسلات Mirage JS ، عليك اختيار أي جهاز تسلسلي مدمج لتبدأ به. سيتأثر هذا القرار بنوع JSON الذي سترسله الواجهة الخلفية في النهاية إلى تطبيق الواجهة الأمامية. تأتي ميراج مع المسلسلات التالية:
-
JSONAPISerializer
هذا المسلسل يتبع مواصفات JSON: API. -
ActiveModelSerializer
يهدف هذا المسلسل إلى محاكاة واجهات برمجة التطبيقات التي تشبه واجهات برمجة تطبيقات Rails التي تم إنشاؤها باستخدام جوهرة active_model_serializer. -
RestSerializer
RestSerializer
هو برنامج Mirage JS "catch all" المتسلسل لواجهات برمجة التطبيقات الشائعة الأخرى.
تعريف المسلسل
لتعريف التسلسل ، قم باستيراد المسلسل المناسب ، مثل RestSerializer
من miragejs
مثل:
import { Server, RestSerializer } from "miragejs"
ثم في مثيل Server
:
new Server({ serializers: { application: RestSerializer, }, })
يتم استخدام RestSerializer
بواسطة Mirage JS افتراضيًا. لذلك من غير الضروري تعيينه بشكل صريح. المقتطف أعلاه لأغراض نموذجية.
دعونا نرى إخراج كل من JSONAPISerializer
و ActiveModelSerializer
على نفس معالج المسار كما حددنا أعلاه
JSONAPISerializer
import { Server, JSONAPISerializer } from "miragejs" new Server({ serializers: { application: JSONAPISerializer, }, })
الإخراج:
{ "data": { "type": "products", "id": "2", "attributes": { "rating": 3, "category": "Electronics", "price": "$1711", "name": "Awesome Product 1" } } }
ActiveModelSerializer
لمشاهدة ActiveModelSerializer في العمل ، أود تعديل إعلان category
في مصنع المنتجات إلى:
productCategory() { let categories = [ 'Electronics', 'Computing', 'Fashion', 'Gaming', 'Baby Products', ]; let randomCategoryIndex = Math.floor( Math.random() * categories.length ); let randomCategory = categories[randomCategoryIndex]; return randomCategory; },
كل ما فعلته هو تغيير اسم الخاصية إلى productCategory
لإظهار كيف سيتعامل المسلسل معها.
بعد ذلك ، نحدد مُسلسل ActiveModelSerializer
على النحو التالي:
import { Server, ActiveModelSerializer } from "miragejs" new Server({ serializers: { application: ActiveModelSerializer, }, })
يقوم المسلسل بتحويل JSON الذي تم إرجاعه كـ:
{ "rating": 2, "product_category": "Computing", "price": "$64", "name": "Awesome Product 4", "id": "5" }
ستلاحظ أن productCategory
قد تم تحويله إلى فئة product_category
والتي تتوافق مع جوهرة active_model_serializer في نظام Ruby البيئي.
تخصيص المسلسلات
يوفر Mirage القدرة على تخصيص جهاز تسلسلي. لنفترض أن تطبيقك يتطلب أسماء السمات الخاصة بك ، يمكنك تجاوز RestSerializer
لتحقيق ذلك. سنستخدم مكتبة المرافق lodash
:
import { RestSerializer } from 'miragejs'; import { camelCase, upperFirst } from 'lodash'; serializers: { application: RestSerializer.extend({ keyForAttribute(attr) { return upperFirst(camelCase(attr)); }, }), },
يجب أن ينتج عن ذلك JSON بالشكل:
{ "Rating": 5, "ProductCategory": "Fashion", "Price": "$1386", "Name": "Awesome Product 4", "Id": "5" }
تغليف
لقد فعلتها! نأمل أن تكون قد حصلت على فهم أعمق لـ Mirage عبر هذه المقالة ورأيت أيضًا كيف أن استخدام المصانع والتركيبات والمسلسلات سيمكنك من إنشاء المزيد من نماذج API الشبيهة بالإنتاج باستخدام Mirage.
- الجزء 1: فهم نماذج وجمعيات Mirage JS
- الجزء الثاني: فهم المصانع والتركيبات والمسلسلات
- الجزء 3: فهم التوقيت والاستجابة والعبور
- الجزء 4: استخدام Mirage JS و Cypress لاختبار واجهة المستخدم