We are still cooking the magic in the way!
استراتيجية الاختبار في التكامل المستمر
استراتيجية الاختبار في التكامل المستمر
خط أنابيب CI بلا استراتيجية اختبار متماسكة ليس سوى طريقة آلية لشحن البرمجيات المعطوبة بشكل أسرع. الهدف من الاختبار في CI ليس تشغيل كل فحص ممكن على كل إيداع — بل هو الحصول على أعلى ثقة بأدنى زمن انتظار في كل مرحلة. يعلّمك هذا الدرس كيف تهيكل الفرق الهندسية في كبرى شركات التقنية اختباراتها، وكيف توازيها على نطاق واسع، وكيف تُزيل الاهتزاز الذي يدمر الثقة في خطوط الأنابيب.
هرم الاختبار في خطوط الأنابيب
يعرّف هرم الاختبار ثلاث طبقات حسب السرعة والتكلفة والنطاق. في خط أنابيب CI، يهم الشكل لأن الاختبارات الأبطأ تستهلك وقت المُشغِّل وتؤخر التغذية الراجعة للمطور.
في خط أنابيب CI، يُترجم الهرم مباشرةً إلى بوابات متدرجة:
- المرحلة 1 — اختبارات الوحدة: تُشغَّل على كل إيداع، ويجب أن تنتهي في أقل من دقيقتين. لا شبكة ولا قاعدة بيانات ولا خدمات خارجية. احتل كل شيء بالنماذج الوهمية.
- المرحلة 2 — اختبارات التكامل: تعمل مقابل خدمات حقيقية (قاعدة بيانات، وسيط رسائل) تُشغَّل كحاويات مرافقة. الميزانية المقبولة: 5 إلى 10 دقائق.
- المرحلة 3 — اختبارات E2E / العقود / الدخان: تعمل فقط عند الدمج في الفرع الرئيسي أو فروع الإصدار. استخدم بيئة مخصصة. ميزانية الوقت: حتى 30 دقيقة.
موازاة الاختبارات على نطاق واسع
تشغيل اختبارات متخلة في مُشغِّل واحد لمدة 40 دقيقة أمر غير مقبول. على نطاق كبرى الشركات — Google وMeta وUber — تعدّ موازاة الاختبارات مصدر قلق هندسي من الدرجة الأولى. الاستراتيجيات التالية متاحة لكل فريق اليوم.
1. بناءات المصفوفة — تقسيم ملفات الاختبار أو مجموعاتها عبر مُشغِّلين مُعلَنين في مصفوفة. GitHub Actions يجعل هذا أصليًا:
يسجّل علم --store-durations المدة التي استغرقها كل ملف اختبار. في التشغيل التالي، يستخدم pytest-split تلك المدد لموازنة الشظايا بالوقت لا بعدد الملفات، مما يضمن انتهاء كل مُشغِّل في تقريبًا نفس اللحظة.
2. حاويات الخدمة لاختبارات التكامل — تشغيل بنية تحتية حقيقية كحاويات مرافقة في نفس المهمة. لا تستخدم قاعدة بيانات مرحلة مشتركة لـ CI؛ استخدم حاويات مؤقتة تُدمَّر بعد المهمة:
--health-cmd، قد تحاول خطوة ما الاتصال قبل أن تقبل قاعدة البيانات الاتصالات، مما يتسبب في فشل غير حقيقي. الخيار options في المثال أعلاه يجعل Actions تنتظر حتى تكون Postgres جاهزة فعلًا.
إدارة الاختبارات المتذبذبة
الاختبار المتذبذب هو الذي يجتاز ويفشل بشكل غير حتمي على نفس الكود. التذبذب هو أكبر مدمّر للثقة في CI. عندما يتوقف المهندسون عن الاعتقاد بأن البناء الأحمر يعني فشلًا حقيقيًا، يبدؤون في دمج كود معطوب. حدّد مدونة هندسة Google التذبذب باعتباره من أكبر خمسة مصادر لإهدار الإنتاجية عبر جميع الفرق الهندسية.
الأسباب الجذرية (بترتيب التكرار على نطاق واسع):
- الاعتمادية الزمنية —
sleep(1)بدلًا من الاستطلاع عن حالة معينة؛ سباقات الظروف في الكود غير المتزامن. - الحالة القابلة للتحول المشتركة — اختبارات تُسرِّب صفوف قاعدة البيانات أو الذاكرة المؤقتة أو المتغيرات العامة للاختبار التالي.
- الاعتمادية على الشبكة — اختبارات تستدعي واجهات برمجية خارجية حقيقية قد تُقيِّد أو تنتهي مهلتها.
- الاعتمادية على الترتيب — اختبارات تنجح فقط عند تشغيلها بتسلسل معين.
- استنفاد الموارد — اختبارات تفشل عندما يكون المُشغِّل تحت ضغط المعالج أو الذاكرة.
استراتيجية الكشف: شغّل كل اختبار من 10 إلى 20 مرة بشكل معزول في بيئة نظيفة. pytest يمتلك إضافة لهذا:
نمط العزل — لا تحذف اختبارًا متذبذبًا ولا تتركه يعترض خط الأنابيب. ضع علامة عليه، وعزله، وأصلحه بموعد محدد:
أضف -m "not flaky" إلى استدعاء خط الأنابيب الرئيسي حتى لا تعترض الاختبارات المعزولة عمليات الدمج. شغّل العلامة flaky منفصلةً في مهمة cron ليلية حتى يرى الفريق الفشل دون أن يُعاق:
إعداد التقارير والرؤية
يجب أن تكون نتائج الاختبار قابلة للاستهلاك دون قراءة السجلات الخام. أصدر JUnit XML من كل مُشغِّل اختبار — كل منصة CI كبرى (GitHub Actions، وGitLab CI، وJenkins، وCircleCI) يمكنها تحليله أصليًا وعرض نجاح/فشل كل اختبار مع سجله التاريخي:
تتبّع معدل تذبذب الاختبار واتجاه متوسط مدة الاختبار وتغيّر التغطية لكل طلب سحب بمرور الوقت. عندما ينمو متوسط وقت الاختبار أكثر من 20% أسبوعيًا، لدى الفريق مشكلة موازاة يجب حلها قبل أن تصبح مشكلة ثقافة.
--cov-fail-under على خط الأساس الحالي مطروحًا منه 2%. طلب السحب الذي يُخفّض التغطية بنسبة 15% يجب أن يفشل. الذي لا يُضيف اختبارات لكود جديد يجب أن يفشل. لكن مطاردة تغطية 100% تحفّز اختبار تفاصيل التنفيذ لا السلوك.
تجميع كل شيء معًا
تبدو استراتيجية اختبار CI الجاهزة للإنتاج لخدمة Python متوسطة الحجم هكذا من البداية للنهاية: تعمل اختبارات الوحدة أولًا على 4 شظايا متوازية (هدف: أقل من 90 ثانية إجمالًا)، وتعمل اختبارات التكامل في مهمة واحدة مع Postgres وRedis كحاويات مرافقة (هدف: أقل من 8 دقائق)، وتعمل اختبارات دخان E2E فقط على الدفع إلى main مقابل بيئة مرحلة (هدف: أقل من 20 دقيقة). الاختبارات المتذبذبة تُعزَل فورًا وتُتابَع في اجتماع مراجعة التذبذب الأسبوعي. التغطية مُقيَّدة عند 78% (خط أساس الفريق). تُنشر جميع التقارير كقطع أثرية JUnit XML وتُعرض في لوحة CI. هذا هو المعيار الذي تعمل به كل فرق جادة — والأساس الذي يجب أن تبني نحوه.