الاختبارات التي تعتمد على واجهة برمجة تطبيقات (API) حية هي اختبارات تفشل لأسباب خاطئة. قد يتعطل خادم تجريبي، أو يتم تطبيق حد معدل من طرف ثالث، أو يقوم زميل بتغيير سجل، وفجأة تصبح مجموعتك حمراء على الرغم من أن التعليمات البرمجية الخاصة بك سليمة. يؤدي محاكاة واجهة برمجة التطبيقات إلى إزالة هذا الضعف. فأنت تستبدل نقطة النهاية الحقيقية ببديل متحكم فيه يعيد الاستجابة التي تطلبها بالضبط، في كل مرة.
يرشدك هذا الدليل عبر الخطوات الفعلية لمحاكاة واجهة برمجة تطبيقات (API) للاختبار. ستحدد مخططًا، وتنشئ استجابات وهمية، وتوجه رمز الاختبار الخاص بك إلى المحاكاة، وتغطي مسارات الأخطاء التي نادرًا ما ينتجها الخادم الحقيقي عند الطلب. تستخدم الأمثلة واجهة برمجة تطبيقات صغيرة لإدارة الطلبات، لكن سير العمل ينطبق على أي خدمة REST أو GraphQL.
متى تكون محاكاة واجهة برمجة التطبيقات هي الخيار الصحيح
قم بمحاكاة واجهة برمجة التطبيقات عندما يكون الشيء الذي تريد اختباره هو التعليمات البرمجية الخاصة بك، وليس الشبكة. تندرج اختبارات الوحدات ومعظم اختبارات التكامل ضمن هذه الفئة. أنت تريد أن تعرف أن عميلك يحلل استجابة 200 بشكل صحيح، ويعيد المحاولة عند استجابة 503، ويعرض رسالة واضحة عند استجابة 404. لا يتطلب أي من ذلك خادمًا حقيقيًا.
احتفظ بواجهة برمجة التطبيقات الحقيقية لاختبارات العقود وطبقة رقيقة من الفحوصات الشاملة (end-to-end). توجد هذه الفحوصات للتأكد من أن المحاكاة لا تزال تتطابق مع الواقع. إذا قمت بمحاكاة كل شيء ولم تتحقق أبدًا مقابل الخدمة المباشرة، فستظل مجموعتك خضراء بينما يتعطل الإنتاج. الانقسام تقريبًا هو: المحاكاة للسرعة والعزل، والوصول إلى الشيء الحقيقي لتأكيد العقد. لإلقاء نظرة أعمق على مكان كل منها، راجع هذا التحليل للسيناريوهات التي تؤتي فيها محاكاة واجهة برمجة التطبيقات ثمارها والتمييز بين خادم المحاكاة والخادم الحقيقي.
نظرة سريعة على سير العمل المكون من خمس خطوات
تتضمن محاكاة واجهة برمجة تطبيقات للاختبار نفس الخطوات الخمس في كل مرة، بغض النظر عن اللغة أو الإطار المستخدم:
- حدد المخطط (schema) بحيث تعكس المحاكاة شكل الاستجابة الحقيقية.
- أنشئ استجابات وهمية، ثابتة أو ديناميكية، من هذا المخطط.
- شغّل خادم محاكاة (mock server) يقدم هذه الاستجابات عبر عنوان URL.
- وجه اختباراتك إلى المحاكاة عن طريق جعل عنوان URL الأساسي قابلاً للتكوين.
- اختبر مسارات الأخطاء التي لن ينتجها الخادم الحقيقي عند الطلب.
يرشدك بقية هذا الدليل عبر كل خطوة بمثال عملي: واجهة برمجة تطبيقات صغيرة لإدارة الطلبات تحتوي على نقطة نهاية GET /orders/{id}. ضع نقطة النهاية هذه في اعتبارك كخط رابط للأمثلة.
الخطوة 1: تحديد المخطط (Schema)
لا تكون المحاكاة مفيدة إلا إذا عكست شكل الاستجابة الحقيقية. ابدأ من مخطط. إذا كانت واجهة برمجة التطبيقات الخاصة بك تحتوي بالفعل على مستند OpenAPI، فاستخدمه. وإذا لم يكن الأمر كذلك، فاكتب واحدًا لنقطة النهاية قيد الاختبار. إليك تعريف مختصر لـ GET /orders/{id}:
paths:
/orders/{id}:
get:
parameters:
- name: id
in: path
required: true
schema: { type: string }
responses:
'200':
content:
application/json:
schema:
type: object
properties:
id: { type: string }
status: { type: string, enum: [pending, shipped, delivered] }
total: { type: number }
items: { type: array, items: { type: object } }
'404':
description: Order not found
يقوم المخطط بوظيفتين. يخبر المحاكاة بالحقول التي يجب إرجاعها، ويوفر لك مصدرًا واحدًا للحقيقة. عندما يغير الواجهة الخلفية العقد، تقوم بتحديث المخطط وتظل كل محاكاة مستمدة منه دقيقة. المحاكاة القائمة على المخطط هي ما يحافظ على مصداقية اختبار عقد واجهة برمجة التطبيقات.
الخطوة 2: إنشاء استجابات وهمية
لديك طريقتان لإنتاج نص الاستجابة.
الاستجابات الثابتة هي JSON ثابت. تقوم بكتابة الحمولة (payload) الدقيقة مرة واحدة وتعيدها المحاكاة دون تغيير. إنها قابلة للتنبؤ وسهلة التأكيد عليها، مما يجعلها مثالية لاختبارات الوحدات حيث تريد قيمة واحدة معروفة.
يتم إنشاء الاستجابات الديناميكية لكل طلب. بدلاً من ترميز "total": 149.99، تملأ المحاكاة الحقول بقيم واقعية مولدة: معرف فريد عالمي (UUID) لـ id، وعضو تعداد عشوائي لـ status، ومبلغ عملة معقول لـ total. تلتقط البيانات الديناميكية الأخطاء التي تخفيها حمولة ثابتة واحدة، مثل المحلل الذي يتعطل على السلاسل الطويلة أو الحقول الفارغة غير المتوقعة.
تستخدم معظم الفرق كليهما. الحمولات الثابتة للاختبارات التي تركز على التأكيدات، والتوليد الديناميكي لتغطية على غرار الغموض (fuzz-style). مع Apidog، لا تكتب أيًا منهما يدويًا. يقرأ Apidog المخطط ويولد نقطة نهاية وهمية تلقائيًا، مطابقة أسماء الحقول مثل email أو phone أو avatar لنوع البيانات الصحيح. تقوم بتوجيه متصفح إلى عنوان URL الخاص بالمحاكاة وتحصل على استجابة صالحة على الفور.
عندما تكتب الحمولات يدويًا، اجعلها واقعية. طلب اختبار بقيمة "total": 0 ومصفوفة items فارغة يمرر محللًا ساذجًا ولكنه يخفي الأخطاء. استخدم قيمًا تشبه الإنتاج: إجمالي يبدو حقيقيًا، واثنين أو ثلاثة عناصر، وحالة موجودة بالفعل في التعداد. كلما كانت بيانات المحاكاة أقرب إلى ما تعيده طلبات حقيقية، زادت قيمة اختبارك.
الخطوة 3: تشغيل خادم المحاكاة
لا قيمة لاستجابة المحاكاة حتى يتم تقديمها. لديك خياران للاستضافة.
يعمل خادم محاكاة محلي على جهازك، عادةً على منفذ مثل localhost:4010. إنه سريع، ويعمل دون اتصال بالإنترنت، وهو الإعداد الافتراضي لاختبارات الوحدات والتكامل. أدوات خفيفة الوزن مثل Prism تقوم بتشغيل خادم محاكاة مباشرة من ملف OpenAPI:
prism mock openapi.yaml
# Mock server listening on http://127.0.0.1:4010
يحتوي خادم المحاكاة السحابي على عنوان URL عام. استخدم واحدًا عندما يحتاج تطبيق جوال، أو مشغل تكامل مستمر (CI runner)، أو متعاون خارجي إلى استدعاء المحاكاة دون الوصول إلى جهاز الكمبيوتر المحمول الخاص بك. يمنح Apidog كل مشروع عنوان URL لمحاكاة سحابية مستضافة، بحيث يمكن لزميل في شبكة أخرى الوصول إلى نفس نقطة النهاية التي تصل إليها أنت.
لعمليات تشغيل الاختبار، فضل المحلي. ليس لديه زمن وصول للشبكة ولا حالة مشتركة، لذلك لا تتصادم عمليتان بناء أبدًا. استخدم الخيار السحابي للعروض التوضيحية والاختبار عبر الأجهزة.
الخطوة 4: توجيه اختباراتك إلى المحاكاة
الآن اربط رمز الاختبار بالمحاكاة بدلاً من الإنتاج. النهج الأكثر نظافة هو عنوان URL أساسي قابل للتكوين. اقرأه من متغير بيئة بحيث يتم تشغيل نفس ملف الاختبار مقابل المحاكاة محليًا وواجهة برمجة التطبيقات الحقيقية في مهمة عقد.
// orderClient.test.js
import { getOrder } from './orderClient.js';
const BASE_URL = process.env.API_BASE_URL || 'http://127.0.0.1:4010';
test('parses a shipped order', async () => {
const order = await getOrder('order_8842', BASE_URL);
expect(order.status).toBe('shipped');
expect(typeof order.total).toBe('number');
});
يأخذ العميل عنوان URL الأساسي كمعامل؛ لا يوجد شيء في رمز الإنتاج يعرف أنه يتم محاكاته. في التكامل المستمر (CI)، تقوم بتعيين API_BASE_URL إلى عنوان المحاكاة قبل تشغيل المجموعة. يحافظ هذا النمط على المحاكاة خارج منطق تطبيقك تمامًا، وهو المكان الذي تنتمي إليه. إذا قمت بتشغيل الاختبارات عبر مسار (pipeline)، تنطبق نفس الفكرة عندما تقوم بأتمتة اختبارات واجهة برمجة التطبيقات في CI/CD.
الخطوة 5: اختبار مسارات الأخطاء
هذه هي الخطوة التي تتجاهلها معظم الفرق، وهي الخطوة التي تؤتي ثمارها. نادرًا ما يعيد الخادم الحقيقي استجابة 500 عندما تريده. بينما تعيد المحاكاة واحدة عند الطلب.
قم بتكوين المحاكاة لتقديم استجابات فشل محددة، ثم تأكد من أن عميلك يتعامل مع كل منها:
| السيناريو | استجابة المحاكاة | ما تؤكده |
|---|---|---|
| سجل مفقود | 404 |
يرمي العميل خطأ واضحًا "لم يتم العثور عليه" |
| فشل الخادم | 500 |
يعيد العميل المحاولة، ثم يعرض احتياطيًا |
| تجاوز حد المعدل | 429 مع Retry-After |
يتراجع العميل بالمقدار الصحيح |
| استجابة بطيئة | 200 بعد تأخير 5 ثوانٍ |
ينتهي وقت العميل ويستعيد |
| نص غير صالح | 200 مع JSON معطوب |
يفشل العميل بأناقة، دون تعطل |
تسمح لك قواعد المحاكاة المتقدمة في Apidog بإرجاع استجابات مختلفة بناءً على الطلب، لذلك يؤدي طلب order_404 إلى 404 بينما تعيد كل معرف آخر استجابة 200 عادية. يمنحك ذلك نقطة نهاية محاكاة واحدة تغطي المسار الناجح والأخطاء. قم بإقران هذا بـ تأكيدات واجهة برمجة التطبيقات القوية وستتحقق مجموعتك من السلوك، وليس فقط رموز الحالة.
تنظيم المحاكاة عبر مجموعة اختبار متنامية
نقطة نهاية محاكاة واحدة سهلة. أما مائة منها، موزعة عبر مجموعة اختبار، فهي النقطة التي تفقد فيها الفرق السيطرة. بعض العادات تحافظ على المجموعة قابلة للإدارة.
قم بتجميع المحاكاة حسب الخدمة الحقيقية التي تمثلها، وليس حسب الاختبار الذي يستخدمها. عندما تتغير واجهة برمجة تطبيقات الدفع، فإنك تريد مكانًا واحدًا للتحديث، وليس عشرين ملف اختبار. سمّي تركيبات المحاكاة (mock fixtures) باسم السيناريو الذي تمثله، مثل order-shipped أو order-rate-limited، بحيث يكون الاختبار الفاشل واضحًا للقراءة. احتفظ بتعريفات المحاكاة في نظام التحكم بالإصدار بجانب الاختبارات، نظرًا لأن المحاكاة جزء من الاختبار وتستحق نفس المراجعة.
قاوم الرغبة في إعطاء كل اختبار حمولة مخصصة خاصة به. معظم الاختبارات تريد نفس كائن الطلب الواقعي مع تغيير حقل واحد. حدد استجابة أساسية مرة واحدة وتجاوز فقط الحقل قيد الاختبار. وهذا يحافظ على سهولة قراءة المجموعة ويعني أن تغيير العقد يؤثر على تعريف أساسي واحد بدلاً من نسخ متناثرة. نفس الانضباط الذي يجعل مجموعة اختبار لأتمتة واجهة برمجة التطبيقات قابلة للصيانة ينطبق مباشرة على المحاكاة التي تدعمها.
الحفاظ على مصداقية المحاكاة
تنجرف المحاكاة. يضيف الواجهة الخلفية حقلاً، يعيد تسمية total إلى amount، أو يغير تعدادًا، وتستمر المحاكاة الخاصة بك في إرجاع الشكل القديم. تمر الاختبارات؛ ويفشل الإنتاج. هذه هي الطريقة الأكثر شيوعًا لفشل المحاكاة، وهي صامتة. لا شيء في مجموعتك يشتكي، لأن المجموعة تقيس المحاكاة مقابل نفسها.
هناك عادتان تمنعان ذلك. أولاً، استمد المحاكاة من نفس المخطط الذي تنشره الواجهة الخلفية، بحيث يؤدي تغيير العقد إلى تحديث كليهما في وقت واحد. المحاكاة التي يتم إنشاؤها من ملف OpenAPI تُعاد إنشاؤها عندما يتغير هذا الملف؛ المحاكاة التي تُكتب يدويًا لا تفعل ذلك. ثانيًا، قم بتشغيل مجموعة صغيرة من اختبارات العقود مقابل واجهة برمجة التطبيقات الحقيقية وفقًا لجدول زمني. وظيفتها الوحيدة هي التأكد من أن الاستجابة الحية لا تزال تتطابق مع المخطط الذي تستخدمه المحاكاة الخاصة بك. عندما تفشل، تعرف أن المحاكاة قديمة قبل أن يدرك المستخدمون.
يساعد أيضًا مراجعة المحاكاة أثناء مراجعة الكود. عندما يغير طلب سحب استجابة واجهة برمجة تطبيقات، يجب على المراجع التحقق من أن المحاكاة المطابقة قد تغيرت أيضًا. معاملة المحاكاة كجزء من العقد، بدلاً من مساعد اختبار يمكن التخلص منه، هو ما يحافظ على موثوقية مجموعة المحاكاة على مدار أشهر من التغييرات.
إذا كنت تريد بيئة واحدة تحتوي على المخطط، وتنشئ المحاكاة، وتشغل الاختبارات مقابلها، فـ قم بتنزيل Apidog. يحافظ على مزامنة التصميم، وخادم المحاكاة، ومجموعة الاختبار، بحيث تكون المحاكاة التي تختبر مقابلها دائمًا هي العقد الحالي. للحصول على خيارات أوسع، قارن المجال في هذا التجميع من أدوات محاكاة واجهة برمجة تطبيقات REST.
الأسئلة المتكررة
هل يجب علي محاكاة واجهة برمجة التطبيقات لكل اختبار؟
لا. قم بالمحاكاة لاختبارات الوحدات والتكامل حيث تقوم بفحص التعليمات البرمجية الخاصة بك. احتفظ بمجموعة صغيرة من اختبارات العقود والاختبارات الشاملة (end-to-end) التي تستدعي واجهة برمجة التطبيقات الحقيقية، حيث تؤكد هذه الاختبارات أن محاكاتك لا تزال تتطابق مع الإنتاج. محاكاة كل شيء تخفي انحراف العقد.
ما الفرق بين استجابة المحاكاة الثابتة والديناميكية؟
الاستجابة الثابتة هي حمولة JSON ثابتة لا تتغير أبدًا، وهو أمر جيد للتأكيدات المتوقعة. يتم إنشاء الاستجابة الديناميكية لكل طلب بقيم واقعية، مما يلتقط الأخطاء التي قد تفوتها حمولة ثابتة واحدة. تستخدم معظم الفرق كليهما.
كيف أتأكد من أن المحاكاة الخاصة بي تظل دقيقة؟
أنشئ المحاكاة من نفس المخطط الذي تستخدمه الواجهة الخلفية، ويفضل أن يكون مستند OpenAPI. ثم قم بتشغيل اختبارات عقود مجدولة مقابل واجهة برمجة التطبيقات الحقيقية للتأكد من أن الاستجابة الحية لا تزال تتطابق مع هذا المخطط. إذا فشلت، تحتاج المحاكاة الخاصة بك إلى التحديث.
هل يمكن للمحاكاة محاكاة الاستجابات البطيئة أو الفاشلة؟
نعم، وهذا أحد أقوى الأسباب للمحاكاة. يمكنك تكوين محاكاة لإرجاع 500، أو 429 مع ترويسة Retry-After، أو 200 متأخرة. يتيح لك ذلك التحقق من منطق إعادة المحاولة والمهلات التي لن يقوم خادم حقيقي سليم بتشغيلها عند الطلب.
خادم محاكاة محلي أم خادم محاكاة سحابي للاختبار؟
استخدم خادم محاكاة محلي لعمليات تشغيل الاختبار. إنه سريع، ولا يحتوي على زمن وصول للشبكة، ويتجنب الحالة المشتركة بين عمليات البناء. استخدم محاكاة مستضافة على السحابة عندما يحتاج جهاز محمول، أو مشغل تكامل مستمر (CI runner)، أو متعاون خارجي إلى الوصول إلى المحاكاة دون الوصول إلى جهازك.
