لماذا تتعثّر أتمتة API عند النقاط الأربع نفسها
تبدأ كل الفرق بالطريقة نفسها. يكتب أحدهم طلبًا موجّهًا إلى نقطة طرفية واحدة، ويتحقّق من رمز الحالة، ثم يضعه في التكامل المستمر. فينجح الأمر. ثم تكبر حزمة الاختبارات، فتظهر أربع مشكلات بالترتيب نفسه تقريبًا لدى الجميع.
تنتهي صلاحية الرموز، فتنجح الحزمة صباحًا وتفشل وقت الغداء. وتحتوي الاستجابات على قيم يحتاجها الطلب التالي، فإمّا أن تُثبَّت هذه القيم داخل الاختبارات وإمّا أن تنكسر الاختبارات. وهناك نقاط طرفية لا يمكن استدعاؤها قبل إنشاء شيء آخر، فيصبح الترتيب مهمًّا دون أن يكون أحد قد دوّنه. ثم إن كل تشغيل يخلّف وراءه صفوفًا، فيسلك التشغيل العاشر سلوكًا مختلفًا عن الأول.
لا شيء من هذا استثنائي؛ فهذه هي الصورة الاعتيادية لأي واجهة خلفية حقيقية. والفارق بين حزمة اختبارات تدوم وأخرى تُحذف بعد ربع سنة هو ما إذا كانت هذه الأربع قد صُمِّم لها منذ البداية أم اكتُشفت لاحقًا.
ابدأ ممّا تكشفه الخدمة فعليًا
قبل كل ذلك تحتاج إلى جرد. فكتابة الاختبارات من الذاكرة تنتج حزمة تغطّي النقاط الطرفية التي صادف أن خطرت لك، وهي عادةً النقاط التي كانت تعمل أصلًا.
يمنحك مدخلان صورة حقيقية. فمستند المواصفات، مثل ملف OpenAPI أو مستند متطلبات المنتج، يخبرك بما يُفترض أن يكون موجودًا. أمّا جولة اكتشاف على الخدمة قيد التشغيل فتخبرك بما هو موجود فعلًا. والفجوة بين الاثنين مثيرة للاهتمام بحدّ ذاتها، وهي نقطة انطلاق لقرارات التغطية أفضل من صفحة بيضاء. في TestSprite تُسمّى هذه الخطوة الأولى API Discovery، وهي تقبل مستند PRD أو توثيق API أو مراقبة الاستدعاءات الحيّة.
ومن هناك تختار النقاط الطرفية التي تستحقّ التغطية بدل قبول كل شيء. فالخطة المولّدة التي تضمّ كل مسار إداري وداخلي ليست سوى ضجيج، والضجيج هو ما يجعل حزم الاختبارات تفقد جمهورها.
المشكلة الأولى: المصادقة (Auto-Authentication)
الأسلوب الساذج يضع الرمز في متغيّر بيئة ويحدّثه يدويًا حين يلاحظ أحدهم وقوع إخفاقات. وهذا ينجح إلى أن يذهب الشخص الذي يعرف بالأمر في إجازة.
ما تريده بدل ذلك هو أن تنتمي بيانات الاعتماد إلى البيئة لا إلى أي اختبار بعينه، وأن يجري تسجيل الدخول كجزء من التشغيل نفسه. وتسمّي TestSprite ذلك Auto-Authentication: تضبط تدفّق تسجيل الدخول مرة واحدة، فيُجلب رمز جديد قبل كل تشغيل. وثمّة مبدآن أهمّ ممّا يبدوان عليه.
افصل بيانات الاعتماد عن الاختبار. الاختبار الذي يحتوي على كلمة مرور لا يمكن مشاركته، ولا توجيهه إلى بيئة ثانية، ولا إيداعه في المستودع. ينبغي أن تقول الحالة «بصفتي مستخدمًا مسجَّل الدخول»، لا «بصفتي user@example.com بكلمة المرور هذه».
صادِق مرة واحدة في كل تشغيل حيثما تسمح الخدمة بذلك. فأي خدمة لها حدّ لمعدّل تسجيل الدخول ستبدأ برفض حزمة تسجّل الدخول في كل حالة، وتبدو هذه الرفوض تمامًا كعيوب في المنتج. وتشخيص هذا الصنف من الإخفاقات الزائفة يكلّف وقتًا أطول من التصميم الذي يتجنّبه من البداية.
المشكلة الثانية: قيم لا توجد إلا أثناء التشغيل (Dynamic Variables)
يعيد استدعاء الإنشاء معرّفًا، ويحتاجه استدعاءا التحديث والحذف. وإذا لُصق المعرّف داخل الاختبار، فإن الاختبار يصلح مرة واحدة فقط لا أكثر.
الحلّ هو التقاط القيمة من الاستجابة والإشارة إليها لاحقًا، بحيث تنقل الحزمة البيانات إلى الأمام بدل افتراضها. وعمليًا يعني ذلك أن الاختبار لا يقول DELETE /orders/8412. بل يقول: احذف الطلب الذي أنشأه هذا التشغيل، أيًّا كان المعرّف الذي آل إليه.
يبدو هذا بديهيًا حين يُكتب، وهو مع ذلك السبب الأكثر شيوعًا في فشل حزمة كانت ناجحة الشهر الماضي. ولهذه الآلية اسم في TestSprite هو Dynamic Variables: التقاط قيمة من استجابة وحقنها في الطلب التالي، مع اشتقاق الربط بدل صيانته يدويًا.
المشكلة الثالثة: نقاط طرفية يعتمد بعضها على بعض (Dependency Chains)
لا يمكنك إضافة عنصر إلى سلة غير موجودة، ولا إتمام شراء سلة بلا عناصر. فتغطية API الحقيقية ليست قائمة مسطّحة من الاستدعاءات، بل هي مخطّط علاقات.
وحين يكون هذا المخطّط ضمنيًا يقع خطآن: تُنفَّذ الاختبارات بترتيب يصادف نجاحه على جهاز دون آخر، ويُنتج إخفاق مبكّر واحد عشرين إخفاقًا لاحقًا تبدو كلها عيوبًا منفصلة.
وجعل الاعتماديات صريحة يعالج الأمرين معًا. فكل حالة تعلن ما تحتاجه وما تنتجه، ويستنتج التشغيل ترتيبًا آمنًا من ذلك، وعند حدوث إخفاق تحصل على إخفاق واحد وتسع عشرة حالة متخطّاة بدل عشرين صفًّا أحمر عليك قراءتها. وهذا هو الفرق بين Endpoint Tests التي تغطّي مسارًا واحدًا في كل مرة، وIntegration Tests وهي سلاسل متعدّدة الخطوات تغذّي فيها كل استجابة ما يليها.
المشكلة الرابعة: البيانات التي تتركها وراءك (Auto-Cleanup)
الحزمة التي تنشئ سجلات ولا تحذفها أبدًا تتدهور وفق جدولها الخاص. تطول القوائم، وتنحرف تحقّقات الترقيم الصفحي، وتبدأ قيود التفرّد بالانطلاق، وينتهي الأمر بأن يفرّغ أحدهم جدولًا يدويًا يوم جمعة.
يجب أن يكون التنظيف جزءًا من التشغيل، مرتبطًا بالموارد التي أنشأها ذلك التشغيل تحديدًا. أمّا التنظيف بالنمط، كحذف كل ما يبدأ اسمه بـ test-، فينجح إلى أن يتداخل تشغيلان أو ينشئ أحد العاملين في الشركة سجلًا باسم سيّئ الحظ. أمّا Auto-Cleanup فيحذف السجلات التي أنشأها التشغيل عند انتهائه، وهذه هي الصيغة التي تصمد أمام التزامن.
كيف تبدو أتمتة API الناجحة من طرفها إلى طرفها
بجمع الأربعة معًا، تبدو الإعدادات التي تصمد على مدى سنة كما يلي.
| مرة واحدة، في البداية | زوّد المشروع بمواصفاتك، ودع جولة اكتشاف تُحصي النقاط الطرفية الحيّة. اختر النقاط التي تستحقّ التغطية. وقاوم التغطية الكاملة في اليوم الأول. واضبط بيانات الاعتماد لكل بيئة، لا لكل اختبار. |
| لكل حالة | التقط القيم التي تحتاجها الخطوة التالية بدل تثبيتها في الشيفرة. أعلن ما تتطلّبه الحالة وما تنتجه. وتحقّق من السلوك لا من رموز الحالة وحدها؛ فاستجابة 200 بمحتوى خاطئ تبقى عيبًا. |
| لكل تشغيل | صادِق مرة واحدة وأعد استخدام الجلسة. ودع مخطّط الاعتماديات يقرّر ترتيب التنفيذ. واحذف عند الانتهاء ما أنشأه هذا التشغيل بالضبط. |
وضعها داخل خطّ الإنتاج
حزمة API التي تعمل حين يتذكّرها أحدهم هي حزمة تعمل قبل الإصدارات لا قبل عمليات الدمج، وهذا هو الطرف الخاطئ. وهناك طريقان للدخول، والاختيار بينهما يتعلّق بمن تريد أن يملك زناد التشغيل.
ومن المفيد أن تعرف قبل الاختيار أن هناك واجهتين تعالجان المسألة بطريقتين مختلفتين. فالبوّابة الإلكترونية هي مسار التوليد والصيانة، حيث يُشتقّ لك الاكتشاف وربط الاعتماديات والتنظيف. أمّا CLI فهو مسار «أحضر شيفرتك معك»: اختبارات الواجهة الخلفية فيه مكتوبة بلغة Python بيدك، وتُعلن بنفسك ما يحتاجه كل اختبار وما ينتجه، ويكون التفكيك اختبارًا تضع عليه هذه العلامة. المشكلات الأربع نفسها، وتقسيم مختلف للعمل. والفرق التي تريد أن تعيش الحزمة داخل مستودعها تختار الثاني عن قصد.
طريقان، والاختيار يتعلّق بالمكان الذي تريد أن تعيش فيه الإعدادات.
طريقان، والاختيار في حقيقته يتعلّق بمن يملك خطّ الإنتاج. فربط GitHub App من لوحة التحكّم لا يتطلّب أي تغيير في مستودعك إطلاقًا، لأنه يتفاعل مع عملية النشر التي تصدرها عملية البناء لديك أصلًا. أمّا إضافة خطوة GitHub Actions فتضع الفحص داخل المستودع حيث يُراجَع كسائر عملية البناء؛ والأوامر الحالية موجودة في مستودع CLI.
قيدان نذكرهما بصراحة
هذا تحقّق وظيفي. والخطط المولّدة تتضمّن فعلًا حالات الحدود والتصريح، فالتشغيل سيخبرك بما إذا كانت قيمة خارج النطاق تُرفض، وبما إذا كان الطلب غير المصادَق عليه يُرفض. لكن هذا ليس اختبار حِمل ولا فحصًا للثغرات. فهو لن يخبرك كيف تتصرّف الخدمة تحت تزامن متواصل، وهو ليس ماسحًا أمنيًا. تلك أدوات منفصلة، والتظاهر بغير ذلك يضيّع عليك بعد ظهرك.
كما أنه لن ينقذ واجهة API بلا مواصفات وبلا أعراف متّسقة. فالاكتشاف يستطيع إحصاء النقاط الطرفية، لكن تحديد معنى الصحيح لكل واحدة منها يظلّ حكمًا، وعلى أحد أفراد فريقك أن يصدره. العمل أقلّ من كتابة الحزمة يدويًا، وهو ليس صفرًا.
كيف تعالج TestSprite المشكلات الأربع
نقاط الإخفاق الأربع في هذه الصفحة ليست عرَضية في اختبار API، ولذلك تُعالَج بوصفها جزءًا من المنتج بدل تركها عبئًا على بنيتك التحتية. فـAuto-Authentication يأخذ تدفّق تسجيل الدخول مرة واحدة ويجلب رمزًا جديدًا قبل كل تشغيل. وDynamic Variables تنقل قيمة من استجابة إلى الطلب التالي. وDependency Chains تشتقّ ترتيب تنفيذ آمنًا ممّا تحتاجه كل حالة وما تنتجه. وAuto-Cleanup يحذف بالضبط السجلات التي أنشأها التشغيل، وهذه هي الصيغة التي تصمد أمام التنفيذ المتوازي.
والتغطية تأتي من مكان مختلف أيضًا. فـAPI Discovery تُحصي ما تكشفه الخدمة قيد التشغيل فعليًا، وPlan Generation تقترح حالات تمتدّ عبر الفئات الوظيفية وفئات المخطّط والتصريح ومعالجة الأخطاء والحدود، ثم تنقّحها أنت بلغة عادية. والنتيجة تتضمّن الحالات السلبية التي لا يكتبها أحد في نهاية سباق تطويري.
وما ينتهي بك ذلك إليه: حزمة تواصل العمل مع تغيّر API، دون ملف conftest أو مجلّد دعم لا يفهمه سوى شخص واحد. أمّا سعة التحمّل وفحص الثغرات فيبقيان أداتين منفصلتين.
هل أحتاج إلى مواصفات OpenAPI للبدء؟
هي مفيدة، وليست شرطًا. فمستند متطلبات المنتج مع جولة اكتشاف على الخدمة قيد التشغيل يوصلانك إلى خطة قابلة للاستخدام. والمواصفات تحسّن أساسًا جودة أول مجموعة حالات مولّدة.
بمَ يختلف هذا عن مجموعة من الطلبات المحفوظة؟
المجموعة تخزّن الطلبات. أمّا المشكلات الأربع في هذا المقال فتتعلّق بالحالة بين الطلبات: الجلسة، والقيم الملتقَطة، والترتيب، والتنظيف. ويمكن جعل المجموعات تتعامل معها بقدر كافٍ من البرمجة النصية، وهو بالضبط عبء الصيانة الذي تحاول معظم الفرق تجنّبه.
هل يمكنه اختبار واجهة API غير متاحة للوصول العام؟
حدّد أي جانب تقصد. فـCLI يستطيع إنشاء نفق إلى تطبيق يعمل على جهازك، وبذلك تكون واجهة أمامية على localhost مغطّاة. لكن هذا الوضع المحلي مخصّص للواجهة الأمامية فقط، فالخدمة الخلفية التي لا يمكن الوصول إليها من الإنترنت تظلّ بحاجة إلى عنوان URL يمكن الوصول إليه من أجل تشغيلات API. ويجدر التأكّد من ذلك على بيئة التجهيز لديك قبل التخطيط للتعميم.
وماذا عن واجهات API الواقعة خلف تسجيل دخول بمصادقة متعدّدة العوامل؟
استخدم حسابًا اختباريًا مخصّصًا بلا عامل ثانٍ، محصورًا ببيئة الاختبار. فأتمتة عامل ثانٍ حقيقي ممكنة، وهي في الغالب الأعمّ أهشّ من أن تستحقّ العناء.
كم عدد النقاط الطرفية التي ينبغي أن تغطّيها الحزمة الأولى؟
غطِّ التدفّقات التي تقلقك قبل الإصدار، لا النقاط الطرفية الأسهل كتابةً. فعشر حالات ذات معنى تعمل مع كل طلب سحب أفضل من مئتي حالة لا يقرأها أحد.
لكلٍّ من الأربعة صفحته الخاصة في توثيق اختبار API إن كنت تريد التفاصيل التقنية بدل المنطق الكامن وراءها.
صمّم للجلسة والحالة والترتيب والتنظيف منذ اليوم الأول.
هذه الأربع هي ما يحوّل حزمة API ناجحة إلى حزمة مهجورة. عالجها صراحةً، وشغّل الحزمة من خطّ الإنتاج لا يدويًا، وأبقِ النطاق محصورًا بالتدفّقات التي تقلقك فعلًا.