تخطَّ إلى المحتوى

🦀 شرح Rust

الاختبارات و Cargo

الدرس 23 من 28· ⏱ 3 دقائق قراءة· 🗓 آخر تحديث: ١٨ أكتوبر ٢٠٢٦

الاختبار مدمج في اللغة

لا تحتاج مكتبة خارجية: أي دالة عليها #[test] اختبار، وcargo test تجدها وتشغّلها. والاصطلاح أن تكون اختبارات الوحدة في نفس الملف بجوار الكود، داخل وحدة tests:

pub fn add(a: i32, b: i32) -> i32 {
    a + b
}

pub fn divide(a: i32, b: i32) -> Result<i32, String> {
    if b == 0 {
        return Err("القسمة على صفر".into());
    }
    Ok(a / b)
}

#[cfg(test)]
mod tests {
    use super::*;           // استورد كل ما في الوحدة الأم

    #[test]
    fn adds_two_numbers() {
        assert_eq!(add(2, 3), 5);
    }

    #[test]
    fn divide_by_zero_is_error() {
        assert!(divide(1, 0).is_err());
    }

    #[test]
    fn divide_works() -> Result<(), String> {
        assert_eq!(divide(10, 2)?, 5);   // ? في الاختبار: Err = فشل
        Ok(())
    }
}
  • #[cfg(test)] تعني "ترجم هذه الوحدة فقط عند الاختبار"، فلا تدخل في البرنامج النهائي.
  • use super::* لأن tests وحدة داخلية، وتحتاج الوصول لما فوقها، حتى الدوال الخاصة. نعم: في Rust تقدر تختبر الدوال غير pub مباشرة.

ماكروهات التحقّق

fn main() {
    let v = vec![1, 2, 3];
    assert_eq!(v.len(), 3);                       // يساوي
    assert_ne!(v[0], 0);                          // لا يساوي
    assert!(v.contains(&2), "يجب أن يحتوي 2");    // شرط مع رسالة
    println!("كل التحقّقات نجحت");
}

عند الفشل، assert_eq! تطبع القيمتين: left: 4, right: 5. ولهذا هي أفضل من assert!(a == b) التي تقول فقط "فشل".

اختبار panic المتوقّع

pub fn get_item(v: &[i32], i: usize) -> i32 {
    v[i]
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    #[should_panic(expected = "index out of bounds")]
    fn out_of_range_panics() {
        get_item(&[1, 2], 5);
    }
}

expected تتأكّد أن الـ panic بالسبب الصحيح، لا أي panic آخر صدفة.

التشغيل

cargo test                    # كل الاختبارات
cargo test divide             # الاختبارات التي يحتوي اسمها "divide"
cargo test -- --nocapture     # اعرض println! من داخل الاختبارات الناجحة
cargo test -- --test-threads=1   # بالتتابع (لاختبارات تتشارك ملفًا مثلًا)

الاختبارات تعمل بالتوازي افتراضيًا. اختباران يكتبان نفس الملف يتعارضان؛ اجعل كل اختبار مستقلًا (ملف مؤقت خاص به).

اختبارات التكامل واختبارات التوثيق

  • اختبارات التكامل: ملفات في مجلّد tests/ بجوار src/. كل ملف صندوق منفصل يرى مكتبتك من الخارج مثل أي مستخدم، أي الواجهة العامة (pub) فقط.
  • اختبارات التوثيق: أمثلة الكود داخل تعليقات /// تُشغَّل كاختبارات مع cargo test، فلا يصبح توثيقك قديمًا بصمت.

أدوات Cargo التي يستخدمها الجميع

cargo check        # فحص الأخطاء بلا توليد ملف تنفيذي: الأسرع أثناء الكتابة
cargo fmt          # تنسيق الكود بالأسلوب الرسمي
cargo clippy       # مئات الفحوصات للأخطاء والأنماط غير الاصطلاحية
cargo doc --open   # توليد التوثيق وفتحه في المتصفّح

في CI، الثلاثي المعتاد: cargo fmt --check ثم cargo clippy -- -D warnings (أي تحذير يُفشل البناء) ثم cargo test.

أخطاء شائعة

  • نسيان use super::*: خطأ cannot find function add in this scope داخل وحدة الاختبار.
  • assert!(a == b): رسالة فشل بلا قيم. استخدم assert_eq!.
  • اختبارات تعتمد على ترتيب تشغيلها أو تتشارك حالة: تفشل عشوائيًا لأنها تعمل بالتوازي.
  • #[should_panic] بلا expected: ينجح الاختبار مع أي panic، حتى غير المقصود.
  • اختبار المسار الناجح فقط: أضف حالات الأخطاء والحدود (فارغ، صفر، أكبر قيمة).

🎯 التالي: الكائنات السمة والإرسال الديناميكي.

شرح الاختبارات و Cargo — Rust بالعربي
الاختبارات و Cargo — Rust بالعربي · The Code Fix

📚 لمزيد من التعمّق في Rust، راجِع كتاب Rust الرسمي.

هل كان هذا الدرس مفيدًا؟