الاختبار مدمج في اللغة
لا تحتاج مكتبة خارجية: أي دالة عليها #[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، حتى غير المقصود.- اختبار المسار الناجح فقط: أضف حالات الأخطاء والحدود (فارغ، صفر، أكبر قيمة).
🎯 التالي: الكائنات السمة والإرسال الديناميكي.