لماذا TDD في Rust ليس مملًا، بل مفيدًا؟
\n\nهل سبق لك أن كتبت كودًا يعمل "على ما يبدو بشكل صحيح"، ثم وجدت خطأ في مكان غير متوقع؟ مألوف. التطوير القائم على الاختبارات (TDD) هو أسلوب يقلب العملية المعتادة رأسًا على عقب. بدلاً من كتابة الكود أولاً ثم التخمين حول ما إذا كان يعمل، تكتب أولاً اختبارًا يجب أن يفشل، ثم تكتب القدر الكافي من الكود ليجتاز الاختبار.
\n\nRust هي لغة تحميك بالفعل على مستوى المترجم من فئة كاملة من الأخطاء (وداعًا لـ segfaults وسباقات البيانات!). ولكن حتى المترجم لا يضمن صحة منطقك. يضيف TDD في Rust طبقة أخرى إلى هذه الأمان: أنت لا تكتب الكود فحسب، بل توثق السلوك المتوقع مباشرة في الاختبارات. وهذا أمر رائع بشكل لا يصدق.
\n\nفي هذه المقالة، سنشرح TDD في Rust بأمثلة حية. سترى كيف تكتب الاختبارات قبل التنفيذ، وكيف تشغلها باستخدام cargo test، وكيف تعيد هيكلة الكود دون خوف من كسر شيء ما. هيا بنا!
ما هو TDD ولماذا Rust مثالي له؟
\n\nTDD تعني Test-Driven Development (التطوير القائم على الاختبارات). الدورة بسيطة وأنيقة:
\n\n- \n
- الأحمر — نكتب اختبارًا يصف وظيفة جديدة. نشغله — يفشل الاختبار (منطقي، لا يوجد كود بعد). \n
- الأخضر — نكتب الحد الأدنى من الكود ليجتاز الاختبار. لا نفكر في الجمال، فقط في اجتياز الاختبار. \n
- إعادة الهيكلة — نحسن الكود دون تغيير سلوكه. تظل الاختبارات خضراء. \n
Rust مثالي لـ TDD لعدة أسباب:
\n- \n
- نظام اختبار مدمج —
cargo testيعمل فورًا، لا حاجة لتثبيت أطر خارجية. \n - الكتابة الصارمة للأنواع — يكتشف المترجم أخطاء الأنواع، وتكتشف الاختبارات الأخطاء المنطقية. معًا لا يُقهرون. \n
- الملكية والاقتراض — تساعد الاختبارات في التأكد من أنك تتعامل مع الذاكرة بشكل صحيح، دون إنشاء نسخ غير ضرورية. \n
- المجتمع والتوثيق — مجتمع Rust يروج بنشاط للاختبارات، والأمثلة وفيرة. \n
إعداد المشروع لـ TDD
\n\nلننشئ مشروعًا جديدًا:
\ncargo new tdd_rust_example\ncd tdd_rust_example\n\nافتراضيًا، يقوم cargo new بإنشاء ملف src/main.rs مع برنامج بسيط. سنكتب مكتبة، لذا سنعيد تسميته إلى src/lib.rs (أو ببساطة نحذف main.rs وننشئ lib.rs). في Cargo.toml لا حاجة لتغيير شيء — الاختبارات مضمنة بالفعل.
بالمناسبة، إذا كنت تكتب مكتبة فقط (بدون ملف تنفيذي)، استخدم cargo new --lib.
الاختبار الأول: الأحمر
\n\nتخيل أننا نريد كتابة دالة تجمع رقمين. وفقًا لـ TDD، نكتب الاختبار أولاً. افتح src/lib.rs وأضف:
#[cfg(test)]\nmod tests {\n #[test]\n fn test_add() {\n let result = add(2, 3);\n assert_eq!(result, 5);\n }\n}\n\nنشغل:
\ncargo test\n\nسترى خطأ في الترجمة — الدالة add غير معرفة. هذا طبيعي. نحن في المنطقة الحمراء.
الأخضر: الحد الأدنى من التنفيذ
\n\nالآن نكتب القدر الكافي من الكود ليجتاز الاختبار. أضف الدالة add فوق وحدة الاختبارات:
fn add(a: i32, b: i32) -> i32 {\n a + b\n}\n\n#[cfg(test)]\nmod tests {\n use super::*;\n \n #[test]\n fn test_add() {\n let result = add(2, 3);\n assert_eq!(result, 5);\n }\n}\n\nلاحظ use super::*; — هذا يستورد كل شيء من الوحدة الأم (الكود الخاص بنا) إلى وحدة الاختبارات.
نشغل cargo test مرة أخرى. أخضر! اجتاز الاختبار. لقد فعلنا الحد الأدنى — أعدنا المجموع. يبدو