Proptest في Rust: دليل شامل مع أمثلة عملية

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

ما هو Proptest ولماذا تحتاجه؟

Proptest هي مكتبة قوية لـ الاختبار القائم على الخصائص (property-based testing) في لغة Rust. على عكس اختبارات الوحدات التقليدية (unit testing) حيث تحدد يدويًا بيانات الإدخال والنتائج المتوقعة، يسمح لك الاختبار القائم على الخصائص بوصف خصائص يجب أن يفي بها برنامجك لـ أي بيانات إدخال صحيحة. تقوم المكتبة تلقائيًا بتوليد العديد من حالات الاختبار العشوائية والتحقق من استيفاء الخصائص المحددة.

لماذا تحتاج إلى هذا؟

  • اكتشاف الأخطاء المخفية: غالبًا ما تجد البيانات العشوائية حالات حافة (edge cases) لم يفكر بها المطور.
  • تقليل حجم كود الاختبار: بدلاً من كتابة عشرات الأمثلة، تكتب خاصية واحدة.
  • التصغير التلقائي (shrinking): إذا فشل الاختبار، يحاول Proptest تلقائيًا العثور على أصغر مجموعة بيانات إدخال تسبب الخطأ.
  • توثيق المواصفات: الخصائص هي تأكيدات رسمية وقابلة للتحقق حول سلوك الكود.

Proptest مستوحى من مكتبة QuickCheck من Haskell، ولكنه مُكيّف لخصائص Rust، بما في ذلك التعامل مع الملكية (ownership)، والاقتراض (borrowing)، والسمات (traits).

التثبيت

Proptest هي مكتبة لـ Rust، لذا يتم تثبيتها عبر مدير الحزم Cargo. أضفها إلى قسم [dev-dependencies] في ملف Cargo.toml الخاص بك:

[dev-dependencies]
proptest = "1.4"

بعد ذلك، نفّذ الأمر cargo build أو cargo test لتحميل المكتبة وتجميعها. يتم استخدام Proptest فقط أثناء الاختبار، لذا فهو لا يزيد من حجم الملف التنفيذي النهائي.

للعمل مع الكود غير المتزامن (tokio، async-std)، قد تحتاج إلى إعداد إضافي، لكن في معظم الحالات، التثبيت القياسي كافٍ.

بداية سريعة مع مثال كود

لنكتب اختبارًا بسيطًا يتحقق من أن دالة reverse (عكس السلسلة النصية) هي عملية انعكاسية: تطبيقها مرتين يُعيد السلسلة الأصلية.

// src/lib.rs
pub fn reverse(s: &str) -> String {
    s.chars().rev().collect()
}

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

    proptest! {
        #[test]
        fn test_reverse_involution(s in ".*") { // ".*" — أي سلسلة نصية
            let reversed = reverse(&s);
            let double_reversed = reverse(&reversed);
            prop_assert_eq!(s, double_reversed);
        }
    }
}

شغّل الاختبارات: cargo test. سيقوم Proptest بتوليد 256 سلسلة نصية عشوائية (افتراضيًا) والتحقق من استيفاء الخاصية. إذا فشل الاختبار، سترى رسالة تحتوي على أصغر مثال مُعيد للإنتاج.

الطرق والوظائف الأساسية

فيما يلي المكونات الرئيسية لـ Proptest. لكل منها وصف، ومعاملات، ومثال.

1. proptest! — ماكرو لتعريف الاختبارات

الوصف: الماكرو الرئيسي الذي يُشغّل الاختبارات القائمة على الخصائص. يستقبل كتلة تحتوي على دوال الاختبار ويُولّد بيانات الإدخال تلقائيًا.

المعاملات: داخل الماكرو، تُعرّف الاختبارات باستخدام السمة #[test]. تُحدد بيانات الإدخال بالصيغة param in strategy.

مثال:

proptest! {
    #[test]
    fn test_addition_commutative(a in 0..100i32, b in 0..100i32) {
        prop_assert_eq!(a + b, b + a);
    }
}

2. prop_assert! و prop_assert_eq! — التأكيدات

الوصف: مشابهة لـ assert! و assert_eq! القياسية، لكنها مُكيّفة لـ Proptest. لا توقف

توصيات