كتابة الاختبارات المؤتمتة (Writing Automated Tests)
في مقاله الصادر عام 1972 بعنوان “المبرمج المتواضع” (The Humble Programmer)، قال إيدجر ديجكسترا (Edsger W. Dijkstra) إن “اختبار البرامج (program testing) يمكن أن يكون وسيلة فعالة للغاية لإظهار وجود الأخطاء البرمجية (bugs)، ولكنه غير كافٍ بشكل يائس لإظهار غيابها”. هذا لا يعني أنه لا ينبغي لنا محاولة الاختبار قدر استطاعتنا!
الصحة (Correctness) في برامجنا هي مدى قيام الكود الخاص بنا بما نعتزم القيام به. تم تصميم Rust مع درجة عالية من الاهتمام بصحة البرامج، لكن الصحة معقدة وليس من السهل إثباتها. يتحمل نظام الأنواع (type system) في Rust جزءاً كبيراً من هذا العبء، لكن type system لا يمكنه التقاط كل شيء. على هذا النحو، تتضمن Rust دعماً لكتابة اختبارات البرمجيات المؤتمتة (automated software tests).
لنفترض أننا كتبنا دالة add_two تضيف 2 إلى أي رقم يتم تمريره إليها. يقبل توقيع هذه الدالة (signature) عدداً صحيحاً كمعلمة (parameter) ويُرجع عدداً صحيحاً كنيتجة. عندما نقوم بتنفيذ وتصريف (compile) هذه الدالة، تقوم Rust بجميع عمليات التحقق من النوع (type checking) والتحقق من الاستعارة (borrow checking) التي تعلمتها حتى الآن للتأكد، على سبيل المثال، من أننا لا نمرر قيمة String أو مرجعاً (reference) غير صالح لهذه الدالة. لكن Rust لا يمكنها التحقق من أن هذه الدالة ستفعل بالضبط ما نعتزم القيام به، وهو إرجاع parameter مضافاً إليه 2 بدلاً من، لنقل، parameter مضافاً إليه 10 أو parameter مطروحاً منه 50! وهنا يأتي دور الاختبارات.
يمكننا كتابة اختبارات تؤكد (assert)، على سبيل المثال، أنه عندما نمرر 3 إلى دالة add_two ، فإن القيمة المرجعة هي 5. يمكننا تشغيل هذه الاختبارات كلما أجرينا تغييرات على الكود الخاص بنا للتأكد من أن أي سلوك صحيح موجود لم يتغير.
الاختبار مهارة معقدة: على الرغم من أننا لا نستطيع تغطية كل التفاصيل حول كيفية كتابة اختبارات جيدة في فصل واحد، إلا أننا سنناقش في هذا الفصل آليات مرافق الاختبار في Rust. سنتحدث عن التوضيحات الشارحة (annotations) والماكرو (macros) المتاحة لك عند كتابة اختباراتك، والسلوك الافتراضي والخيارات المقدمة لتشغيل اختباراتك، وكيفية تنظيم الاختبارات إلى اختبارات الوحدة (unit tests) واختبارات التكامل (integration tests).