مفارقة التغطية 100% — أو لماذا قد تخدعك اختباراتك
\n\nلقد كتبت كودًا مثاليًا بلغة Rust. المترجم (Compiler) راضٍ، و clippy صامت، وكل الاختبارات تمر بنجاح، وتقرير التغطية يخبرك بفخر: تم تغطية 100% من الأسطر. هل يمكنك الاسترخاء؟ لا. اختبار التحور (Mutation Testing) في Rust هو الأسلوب الذي سيكشف لك أن اختباراتك ربما تحمي الهواء، وليس المنطق الفعلي للكود.
\n\nتخيل أنك غيرت عن طريق الخطأ علامة + إلى - في دالة حرجة. هل ما زالت الاختبارات تمر بنجاح؟ تهانينا، لقد وجدت كائنًا متحورًا (Mutant) — وهو عيب يعيش في كودك ولكن اختباراتك لا تكتشفه. في هذه المقالة، سنشرح كيف يساعدك اختبار التحور في Rust في العثور على هذه النقاط العمياء، وسنطبقه في مشروع حقيقي.
ما هو اختبار التحور (ولماذا Rust مثالي له)
\n\nاختبار التحور (Mutation Testing) هو طريقة لتقييم جودة الاختبارات، حيث يتم إجراء تغييرات صناعية صغيرة (تحورات) في الكود الأصلي، ثم يتم تشغيل الاختبارات. إذا لم تفشل الاختبارات على الكود المتحور، فهذا يعني أن الكائن المتحور قد نجا. كلما زاد عدد الكائنات المتحورة الناجية، كانت اختباراتك أسوأ.
\n\nلغة Rust تكتشف وحدها فئات كاملة من الأخطاء في مرحلة الترجمة: مثل حالات السباق (race conditions)، المؤشرات الفارغة (null pointers)، والاستخدام بعد التحرير (use-after-free). لكن اختبار التحور في Rust يستهدف تحديدًا الأخطاء المنطقية — وهي ما لا يراه المترجم. وهذا يجعل Rust بيئة مثالية: أنت محمي بالفعل من 70% من الأخطاء النمطية، بينما يكمل اختبار التحور القضاء على الـ 30% المتبقية.
\n\nكيف يبدو التحور في كود Rust
\n\nتشمل التحورات النمطية ما يلي:
\n- \n
- تغيير العوامل الحسابية:
a + b←a - b\n - عكس الشروط:
if x > 5←if x < 5\n - استبدال الثوابت:
let n = 42←let n = 0\n - حذف استدعاءات الدوال:
process(data)← استدعاء فارغ \n - تغيير حدود التكرار:
0..n←0..=n\n
إذا لم يفشل اختبار واحد على الأقل بعد هذا التحور — فهذا يعني أنك أضعت وقتك في كتابة هذا الاختبار. إنه لا يختبر ما يفترض أن يختبره.
\n\nأدوات اختبار التحور في Rust: cargo-mutants
\n\nحاليًا، الأداة الرئيسية لـ اختبار التحور في Rust هي cargo-mutants. إنها أداة ناضجة، مدعومة بنشاط، وتقدم تقارير سهلة القراءة. التثبيت بسيط:
cargo install cargo-mutants\n\n\nبعد ذلك، يكفي تشغيل الأمر:
\n\ncargo mutants\n\n\nوستقوم الأداة تلقائيًا بالعثور على جميع التحورات في كودك، وتشغيل الاختبارات، وإصدار تقرير HTML مع ترميز لوني: الأخضر للكائنات المتحورة المقتولة (الاختبارات نجحت)، والأحمر للناجية (مشكلة).
\n\nبدائل أخرى
\n- \n
- mutagen — أداة أقدم ولكنها لا تزال تعمل. تحورات أقل، لكنها أسرع في المشاريع الصغيرة. \n
- cargo-insta — ليست اختبار تحور بالضبط، لكنها قريبة: تساعد في فحص التغييرات في لقطات الاختبار بصريًا. \n
للعمل الجاد، أوصي باستخدام cargo-mutants — فهو يوفر أفضل توازن بين السرعة وعمق التحليل.
مثال عملي: اختبار حاسبة الخصومات
\n\nلنفترض أن لدينا وحدة لحساب الخصومات في متجر إلكتروني بلغة Rust:
\n\n// src/discount.rs\n\npub fn calculate_discount(price: f64, customer_age: u32) -> f64 {\n let mut discount = 0.0;\n\n // خصم 10% لكبار السن (أكبر من 65)\n if customer_age > 65 {\n discount += 0.1;\n }\n\n // خصم إضافي 5% للمشتريات التي تزيد عن 1000 ريال\n if price > 1000.0 {\n discount += 0.05;\n }\n\n // الحد الأقصى للخصم لا يتجاوز 30%\n if discount > 0.30 {\n discount = 0.30;\n