محاكاة API والمحاكاة الافتراضية للخدمات
محاكاة الواجهات البرمجية (API Mocking) تعني استبدال واجهة API يعتمد عليها النظام قيد الاختبار ببديل يجيب عن طلبات HTTP بالطريقة التي يحتاجها الاختبار. مثال ذلك خدمة الطلبات في متجر إلكتروني تستدعي مزوّد دفع. ففي بيئة الاختبار يشير عنوان URL الأساسي للمزوّد إلى خادم Stub يجيب عن POST /v1/charges بـ«captured» أو «declined» أو 503 بحسب رمز البطاقة المرسَل، فيُختبر كل مسار دفع دون تحريك أموال حقيقية.
والمحاكاة الافتراضية للخدمات (Service Virtualization) هي الفكرة نفسها على مستوى بيئة اختبار كاملة: تُحاكى أنظمة تابعة بأكملها، غالباً من حركة مرور حقيقية مسجّلة، وتتشاركها عدة فرق. ويُدرج منهج ISTQB للمستوى التأسيسي البدائل (Stubs) والمشغّلات (Drivers) والمحاكيات (Simulators) والمحاكاة الافتراضية للخدمات ضمن عناصر بيئة الاختبار التي تُجهَّز أثناء تهيئة الاختبار (Test Implementation). لقد حدّد اختبار العقد ما تعد به الواجهة، والمحاكاة الجيدة تفي بهذه الوعود بالضبط. ومفردات بدائل الاختبار (Test Doubles) التي عرفتها في اختبار المكوّنات (Stub وSpy وMock وFake) تنتقل هنا من الشيفرة إلى الشبكة.
لماذا يهم
كثيراً ما توقف الاعتماديات الحقيقية الاختبار: خدمة فريق آخر لم تُبنَ بعد، أو بيئة Sandbox لدى طرف ثالث متوقفة، أو استدعاءات تكلّف مالاً أو تصطدم بحدّ معدل الطلبات، أو بيانات مشتركة تتغير بين تشغيل وآخر. والاختبارات التي تعتمد عليها تفشل لأسباب لا علاقة لها بالشيفرة قيد الاختبار.
والأهم أن البدائل تجعل مسارات الخطأ قابلة للاختبار. فمزوّد الدفع الحقيقي لن يعيد 503 أو 429 أو ردّاً بعد 15 ثانية متى طلبت ذلك، مع أن هذه هي اللحظات التي يُخصم فيها المبلغ من العميل مرتين أو تضيع فيها الطلبات. والثمن أن البديل لا يصلح إلا بقدر شبهه بالواجهة الحقيقية؛ فالبديل الخاطئ يعطي اختبارات ناجحة وإخفاقات في بيئة الإنتاج.
Stub وMock وFake حين تكون الاعتمادية واجهة API
يرسل النظام قيد الاختبار طلبات HTTP حقيقية، ويجيب عنها شيء غير الخدمة الحقيقية. ولا يتغير إلا الإعداد: فعنوان URL الأساسي للاعتمادية، مثل PAYMENT_API_URL، يشير إلى البديل في CI وإلى المزوّد الحقيقي في Staging. وفي العمل اليومي على الواجهات تُستخدم كلمة «Mock» بتساهل، و«خادم Mock» يُضبط في الغالب ليعمل كبديل Stub. وحين يهمّ الفرق، اسأل ماذا يفعل البديل فعلاً:
- Stub: يعيد استجابة جاهزة لكل طلب يطابقه، مثل
GET /v1/rates/RUH←200 {"fee": 18}، ولا يتذكر شيئاً. ويتحقق الاختبار من نتيجة نظامك، كالرسوم المعروضة (التحقق من الحالة). - Mock: يجيب كما يجيب Stub، لكنه مبرمج مسبقاً بتوقعات عن الطلبات التي يجب أن تصله، مثل طلب
POST /v1/chargesواحد بالضبط مع ترويسةIdempotency-Key. ويفشل الاختبار إذا غاب الاستدعاء أو تكرر أو اختلف (التحقق من السلوك). والاختبار الذي يقرأ سجل الطلبات بعد ذلك يستخدم البديل بوصفه Spy. - Fake: تنفيذ مبسّط لكنه يعمل، وله حالته الخاصة، في الذاكرة عادةً:
POST /v1/ordersيخزّن الطلب وGET /v1/orders/{id}يعيده. وهو مفيد حين تعتمد الاستدعاءات بعضها على بعض، لكن منطقه نفسه قد يكون خاطئاً. - المحاكاة الافتراضية للخدمات: نظام تابع محاكى داخل بيئة اختبار مشتركة، وغالباً ما يُبنى بتسجيل حركة مرور حقيقية، مع الحالة والتأخير والأعطال، وأحياناً لبروتوكولات غير HTTP. وبناؤه وصيانته أعلى تكلفة، وكثيراً ما يملكه فريق المنصة أو فريق بيئات الاختبار. وبعض الفرق تستخدم المصطلح مرادفاً لمحاكاة الواجهات البرمجية؛ والفرق هو النطاق والملكية.
Stub
يجيب باستجابات جاهزة
- مثال:
GET /v1/rates/RUH←200 {"fee": 18}- ما يتذكره:
- لا شيء: الطلب نفسه يلقى الجواب نفسه دائماً
- ما يتحقق منه الاختبار:
- نتيجة نظامك (التحقق من الحالة)
- انتبه إلى:
- الانحراف عن الواجهة الحقيقية
Mock
يجيب ويتحقق من الطلبات
- مثال:
- طلب
POST /v1/chargesواحد بالضبط مع ترويسةIdempotency-Key - ما يتذكره:
- الطلبات التي وصلته
- ما يتحقق منه الاختبار:
- الطلبات التي أرسلها نظامك (التحقق من السلوك)
- انتبه إلى:
- توقعات صارمة إلى حدّ أن تغييرات غير ضارة تكسر الاختبارات
Fake
تنفيذ مبسّط لكنه يعمل
- مثال:
POST /v1/ordersيخزّن الطلب؛ وGET /v1/orders/{id}يعيده- ما يتذكره:
- بياناته الخاصة، في الذاكرة عادةً
- ما يتحقق منه الاختبار:
- نتيجة نظامك عبر استدعاءات يعتمد بعضها على بعض
- انتبه إلى:
- منطقه نفسه قد يكون خاطئاً
المحاكاة الافتراضية للخدمات
نظام تابع محاكى
- مثال:
- حركة مرور حقيقية مسجّلة، تُعاد مع الحالة والتأخير والأعطال
- ما يتذكره:
- الحالة عبر الاستدعاءات، كما سُجّلت أو ضُبطت
- ما يتحقق منه الاختبار:
- مسارات كاملة في بيئة اختبار مشتركة
- انتبه إلى:
- التكلفة والصيانة؛ وقد تحمل التسجيلات بيانات شخصية ورموز وصول
أدوار نموذجية: كثيراً ما تستخدم الأدوات والفرق هذه الأسماء بتساهل، فاسأل ماذا يفعل البديل فعلاً.
جرّبها: مولّد استجابات API وهمية جرّبها: مولّد بيانات JSON وهمية
متى تستخدم المحاكاة، ومتى تستخدم الواجهة الحقيقية
قرّر لكل اعتمادية ولكل مستوى اختبار: استبدل ما لا تملكه أو لا تتحكم فيه، واستخدم الشيء الحقيقي لما يجب أن يثبته الاختبار. لا تستبدل النظام قيد الاختبار نفسه أبداً. واحتفظ بالاعتمادية الحقيقية أو ببيئة Sandbox لدى المزوّد لفحوص تكامل النظام والفحوص من طرف إلى طرف. ولا تستبدل أبداً ضابط الحماية الذي تختبره ببديل Stub: فالبديل الذي يقبل أي رمز وصول لا يثبت شيئاً عن المصادقة. ويستحق البديل جهده حين:
- لا تكون الاعتمادية موجودة بعد: بديل Stub مبني من مستند OpenAPI متفق عليه يتيح بدء الاختبار مبكراً.
- تكون الاعتمادية لطرف ثالث بطيء، أو يتقاضى أجراً عن كل استدعاء، أو يحدّ معدل الطلبات، أو ليست لديه بيئة Sandbox صالحة.
- تحتاج إلى استجابات لن تعطيها الخدمة الحقيقية عند الطلب:
429معRetry-After، و503، وانتهاء المهلة، وجسم ليس JSON، وقائمة فارغة. - يجب أن تكون الاختبارات سريعة وقابلة للتكرار في CI، أياً كانت حالة البيئات المشتركة.
مثال تطبيقي: حالات تعذّر الدفع عند إتمام الشراء
بيئة Sandbox لدى مزوّد الدفع لا تعيد إلا خصماً ناجحاً أو بطاقة مرفوضة. والمتطلب R-21 ينص على: «البطاقة المرفوضة تُبقي الطلب UNPAID ويُعرض سبب الرفض. وإذا كان المزوّد مشغولاً أو غير متاح، يبقى الطلب UNPAID ويُطلب من العميل المحاولة لاحقاً. وإذا لم يصل رد خلال 10 ثوانٍ، يصبح الطلب PAYMENT_PENDING وتُفحص حالة الخصم لاحقاً، ولا يُخصم المبلغ مرة أخرى أبداً». وتوجّه بيئة الاختبار عنوان URL الأساسي للدفع إلى خادم Stub، مع بديل Stub لكل رمز بطاقة:
- الصفان الأولان فقط يمكن إحداثهما في بيئة Sandbox؛ ومن دون بدائل Stub كانت الصفوف الأربعة الأخرى ستظهر أول مرة على الأرجح في بيئة الإنتاج، أمام عملاء حقيقيين.
tok_slowيحتاج إلى أكثر من Stub: فإلى جانب حالة الطلب، يتحقق الاختبار من وصول طلب خصم واحد بالضبط، حتى بعد أن يضغط العميل «ادفع» مرة ثانية، إما بتوقّع مضبوط مسبقاً في Mock وإما بقراءة سجل الطلبات بعد ذلك (Spy).tok_brokenليس في R-21: لقد جاء من سؤال المختبِر «ماذا لو لم يكن الجسم JSON؟»، واختار الفريق معاملته كمزوّد غير متاح.
Stub server for the payment provider (test environment only)
Request: POST /v1/charges; the stub is chosen by "token" in the body
token Stub response Expected in our system
------------ ------------------------------------ ------------------------------
tok_ok 201 {"status":"captured"} order PAID, receipt sent
tok_declined 422 {"error":"card_declined"} order UNPAID, reason shown
tok_busy 429, Retry-After: 30 order UNPAID, "try again later"
tok_down 503 {"error":"unavailable"} order UNPAID, "try again later"
tok_slow 201, but only after 15 s at 10 s: PAYMENT_PENDING and
exactly one charge request
tok_broken 200, body is an HTML error page handled as unavailable, loggedجرّبها: محاكي رموز حالة API جرّبها: مولّد استجابات خطأ وهمية
مخاطر المحاكاة وكيف تحتويها
البديل افتراض عن نظام آخر مكتوب في صورة شيفرة اختبار. وكل خطر هو طريقة يخطئ بها هذا الافتراض:
- الانحراف (Drift): يعيد المزوّد تسمية حقل أو يغيّر صيغة أخطائه، ويظل البديل يجيب بالطريقة القديمة، فيقع الإخفاق في بيئة الإنتاج بينما تبقى الاختبارات ناجحة. تحقّق من أجسام استجابات بدائل Stub مقابل عقد المزوّد، وشغّل اختبارات عقد ترسل الطلبات نفسها إلى بيئة Sandbox لدى المزوّد وتقارن إجاباتها بإجابات البدائل، وفق جدول منتظم كمرة في اليوم، ومع كل إصدار للمزوّد. والعقود التي يقودها المستهلك (Consumer-Driven Contracts) ويتحقق منها المزوّد تسدّ الثغرة من الجهة الأخرى.
- السلوك غير الواقعي: تجيب البدائل فوراً ولا تحدّ معدل الطلبات أبداً. أضف تأخيراً واقعياً حيث يهمّ التوقيت، واحتفظ ببضعة اختبارات مقابل الخدمة الحقيقية.
- الإفراط في المحاكاة (Over-mocking): بدائل كثيرة إلى حدّ أن الاختبار لا يختبر إلا البدائل، أو توقعات صارمة في الترويسات وترتيب الاستدعاءات إلى حدّ أن تغييرات غير ضارة تكسر الاختبارات.
- التسرّب: عنوان URL لبديل Stub يبقى في إعدادات Staging أو الإنتاج. يجب أن تذكر كل بيئة أيّ الاعتماديات حقيقية، وأن تؤكد فحوص النشر ذلك.
- البيانات المسجّلة: قد تحتوي حركة المرور المسجّلة للمحاكاة الافتراضية للخدمات على بيانات شخصية ورموز وصول. أخفِها قبل تخزينها أو مشاركتها.
أخطاء شائعة
- تسمية كل بديل Mock دون الاتفاق على ما يفعله: فبديل Stub يجيب فقط لا يستطيع كشف خصم مكرر.
- كتابة استجابات بدائل Stub من الذاكرة بدلاً من العقد أو من استجابة حقيقية مسجّلة.
- محاكاة مسار النجاح وحده، فلا تُختبر أبداً حالات انتهاء المهلة وإعادة المحاولة ورسائل الخطأ.
- اعتبار نجاح حزمة اختبارات تعمل على بدائل دليلاً على أن التكامل يعمل.
- ترك البدائل بلا مالك، فلا يحدّثها أحد حين يتغير المزوّد.
- إيداع حركة مرور مسجّلة تحتوي على بيانات شخصية أو رموز وصول حقيقية في المستودع.
من منظور ضمان الجودة
كثيراً ما يحدد المختبِر ما يجب أن تعيده البدائل، لأن الاستجابات القيّمة هي حالات الخطأ التي يصممها. وفي جلسات التنقيح (Refinement) اسأل عن كل اعتمادية خارجية: هل توجد بيئة Sandbox؟ ما الأخطاء والحدود التي يوثقها العقد؟ كيف سنحاكي انتهاء المهلة؟ ومن يملك بدائل Stub؟ وسجّل الإجابات لكل مستوى اختبار في خطة الاختبار.
في التقارير، اذكر هل جاءت النتيجة من بديل أم من الخدمة الحقيقية؛ فالنجاح على بديل Stub دليل أضعف. وأرفق تعريف الـStub بتقارير الأخطاء. والاختلاف بين البديل والواجهة الحقيقية نتيجة تستحق التسجيل: إما أن البديل خاطئ، وإما أن المزوّد أخلّ بعقده. وبعد أي حادثة في بيئة الإنتاج تتعلق باعتمادية، أضف تلك الاستجابة إلى بدائل Stub.
تمرين عملي
خطّط البدائل لواجهة أسعار الشحن
تعرض صفحة إتمام الشراء خيارات التوصيل من مزوّد شحن. باستخدام المتطلب CHK-7 ومقتطف العقد أدناه، خطّط بدائل Stub، وقرّر أيّ بند يحتاج إلى Mock بدلاً من Stub، واذكر ما الذي لا يزال يحتاج إلى بيئة Sandbox الحقيقية وكيف ستكشف الانحراف.
المتطلب CHK-7 (إتمام الشراء، خطوة التوصيل)
1. اعرض خيارات التوصيل مرتبة حسب الرسوم، الأرخص أولاً. وإذا لم يُعِد المزوّد
أي خيار، فاعرض «التوصيل غير متاح لهذا العنوان».
2. إذا لم يُجب المزوّد خلال 5 ثوانٍ، أو أجاب برمز حالة 5xx، فاعرض الرسوم
الثابتة 25.00 ريالاً وسجّل الحدث.
3. خزّن الخيارات مؤقتاً لكل مدينة ووزن مدة 10 دقائق: استدعاء واحد للمزوّد
لكل مدينة ووزن، لا استدعاء لكل عرض للصفحة.
Provider contract (excerpt)
GET /v2/rates?city={code}&weightKg={number}
200 {"rates": [{"service": "standard", "fee": 18.00, "days": 3}, ...]}
400 {"error": "invalid_city"}
429 Retry-After: {seconds}- اكتب استجابات المزوّد التي تختبر كل بند في CHK-7، بما فيها الأخطاء.
- اكتب جدول بدائل Stub: الطلب، واستجابة البديل، والسلوك المتوقع لصفحة إتمام الشراء.
- حدّد السيناريو الذي يحتاج إلى التحقق من الطلبات، وما يتحقق منه الاختبار.
- اكتب ما لا يزال يحتاج إلى بيئة Sandbox الحقيقية، وطريقة واحدة لكشف الانحراف.
إظهار الإجابة النموذجية
البند 1: 200 مع خيارات غير مرتبة عمداً (35.00 و18.00 و22.00) ← تظهر 18.00 أولاً، لأن بيانات Stub المرتبة أصلاً لا تكشف غياب الترتيب؛ و200 مع "rates": [] ← «التوصيل غير متاح لهذا العنوان».
البند 2: 503 ← الرسوم الثابتة 25.00 وقيد في السجل؛ ورد بعد 7 ثوانٍ ← الرسوم الثابتة وقيد في السجل؛ ورد بعد نحو 4 ثوانٍ ← خيارات المزوّد، فيُختبر الحد من جانبيه.
خارج CHK-7: 400 invalid_city و429 مع Retry-After. أنشئ لهما بدائل Stub على أي حال، واسأل مالك المنتج عمّا يجب أن يُعرض في كل منهما.
البند 3 يحتاج إلى Mock أو Spy: فالصفحة تبدو واحدة مع الذاكرة المؤقتة ومن دونها، ولا يُظهر التخزين المؤقت إلا سجل الطلبات. ثلاث زيارات للمدينة والوزن نفسيهما ← طلب GET /v2/rates واحد بالضبط؛ وزيارة بعد 10 دقائق، أو بعد تقديم ساعة الاختبار ← طلب واحد إضافي بالضبط؛ والمدينة نفسها بوزن آخر ← طلب خاص بها، لأن ذاكرة مؤقتة مفتاحها المدينة وحدها ستعرض رسوماً خاطئة.
بيئة Sandbox الحقيقية: بضعة اختبارات تثبت أن المزوّد يقبل الطلب الحقيقي (أسماء المعاملات والمصادقة) وأن استجابته تُحلَّل بنجاح. الانحراف: تحقّق من أجسام استجابات بدائل Stub مقابل مخطط المزوّد في CI، وقارن استجابة حديثة من بيئة Sandbox بالبدائل عند كل إصدار للمزوّد.
أسئلة شائعة
هل خادم Mock هو نفسه Mock؟
ليس بالمعنى الدقيق. فخادم Mock الذي لا يعيد إلا استجابات جاهزة يعمل كبديل Stub. ويصبح Mock أو Spy حين يتحقق الاختبار أيضاً من الطلبات التي وصلته. والأدوات تستخدم الكلمات بتساهل، فصِف ما يفعله البديل بدلاً من الجدال حول اسمه.
ما الفرق بين محاكاة الواجهات البرمجية والمحاكاة الافتراضية للخدمات؟
في الغالب النطاق والملكية. محاكاة الواجهات تعني عادةً بدائل Stub أو Mock يعرّفها فريق لاختباراته. أما المحاكاة الافتراضية للخدمات فتحاكي أنظمة تابعة كاملة داخل بيئة اختبار مشتركة، غالباً من حركة مرور مسجّلة ومع الحالة والتأخير والأعطال، وكثيراً ما يديرها فريق المنصة أو فريق بيئات الاختبار. وبعض الفرق تستخدم المصطلحين بمعنى واحد.
إذا استخدمنا المحاكاة، فهل ما زلنا نحتاج إلى اختبارات العقد والاختبار من طرف إلى طرف؟
نعم. فالمحاكاة تتيح اختبار نظامك بمعزل عن غيره وفرض مسارات الخطأ. واختبارات العقد تتحقق من أن البدائل ما زالت تطابق الواجهة الحقيقية. وبضعة اختبارات على التكامل الحقيقي، في Staging أو في بيئة Sandbox لدى المزوّد، تؤكد أن الربط والمصادقة والإعدادات تعمل. وكل منها يغطي ما لا يغطيه الآخران.
هل يستطيع المختبِر العمل بالمحاكاة دون كتابة شيفرة؟
غالباً نعم. فكثير من خوادم Mock تُضبط بملفات JSON أو YAML أو من واجهة مستخدم، وبديل Stub في جوهره طلب يُطابَق واستجابة تُعاد. وأدوات المحاكاة في Mutqan تولّد أجسام الاستجابات وحمولات الأخطاء لوضعها في تلك الملفات، وأداة API Status Code Simulator تعطيك نقطة نهاية تعيد أي رمز حالة، مع تأخير اختياري يصل إلى 10 ثوانٍ وجسم JSON مخصص. وهذا يكفي لاختبار تعامل العميل مع الأخطاء، ومع انتهاء المهلة حين تكون أقصر من 10 ثوانٍ.
المراجع
- ISTQB Certified Tester Foundation Level (CTFL) v4.0المنهج التأسيسي لاختبار البرمجيات: مستويات الاختبار وأنواعه وتقنيات تصميم الاختبارات وإدارة الاختبار والأدوات.
- Test Double (Martin Fowler)مصطلحات Gerard Meszaros لبدائل الاختبار (Test Doubles) — dummy وfake وstub وspy وmock — كما لخّصها Martin Fowler.
- Mocks Aren't Stubs (Martin Fowler)الفرق بين التحقق من الحالة والتحقق من السلوك، وبين الأسلوب الكلاسيكي وأسلوب الكائنات الوهمية (mockist) في التطوير الموجّه بالاختبار.
- Contract Test (Martin Fowler)اختبارات العقود: فحوص دورية تتأكد من أن الاستدعاءات الموجّهة إلى بديل الاختبار تعيد النتائج نفسها التي تعيدها الخدمة الخارجية الحقيقية.
تدرّب بأدوات متقن
ولّد استجابات API وهمية كاملة (الحالة والترويسات وجسم JSON) لحالات النجاح والقوائم والتفاصيل والأخطاء والنتائج الفارغة.
ولّد استجابات خطأ منظّمة (التحقق والمصادقة وعدم العثور وتحديد المعدّل وخطأ الخادم) بالصيغ الشائعة.
احصل على نقطة نهاية عامة تعيد أي رمز حالة HTTP، مع تأخير اختياري وجسم JSON مخصّص، لاختبار معالجة الأخطاء في العميل.
ولّد بيانات JSON وهمية من قالب يحتوي على عناصر نائبة ({{name}} و{{uuid}} و{{int:1-10}} و{{date}} و{{pick:a|b}}).
تحقّق من استجابات API وفق JSON Schema أو مخطط استجابة OpenAPI.
مقارنة استجابة API فعلية بعقد متوقَّع (مثال أو مخطط) والإبلاغ عن الفروق الكاسرة وغير الكاسرة.
للتعمّق أكثر
تقدّمك في هذا الموضوع
سجّل الدخول لتتابع تقدّمك
علّم الموضوعات مكتملة، وتابع من حيث توقفت، وافتح امتحانات التخصص.