الاختبار الطفري في Java: كسر الكود لتحسينه

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

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

ابدأ الدورة

اختباراتك تكذب وأنت لا تعلم

\n\n

تخيل موقفًا: لقد كتبت اختبارات وحدة، وcode coverage (تغطية الكود) تصل إلى 95%. المدير سعيد، وخط أنابيب CI/CD أخضر. لكن مساء الجمعة يظهر خطأ في الإنتاج. هل هذا مألوف؟

\n\n

المشكلة أن تغطية الكود التقليدية هي وهم. فهي تظهر أي أسطر الكود تم تنفيذها، لكنها لا تجيب على السؤال الرئيسي: هل تختبر الاختبارات المنطق فعلاً؟ هنا يأتي دور mutation testing - وهي منهجية "تكسر" كودك بلا رحمة لتتحقق من جودة اختباراتك.

\n\n

في هذه المقالة، سنشرح ما هو الاختبار الطفري في Java، وكيف يعمل، وسنقدم أمثلة حقيقية باستخدام أداة PIT (Pitest). ستتعلم كيف تحول اختباراتك من "علامة في التقرير" إلى حماية حقيقية من الأخطاء.

\n\n

ما هو الاختبار الطفري (mutation testing) ولماذا هو مهم؟

\n\n

Mutation testing (أو الاختبار الطفري) هو طريقة لتقييم جودة الاختبارات، حيث يتم إدخال تغييرات صغيرة ومتعمدة خاطئة في الكود الأصلي (تسمى الطفرات). بعد ذلك، يتم تشغيل اختباراتك. إذا فشل اختبار واحد على الأقل - تم "قتل" المتحول، والاختبار نجح. أما إذا نجحت الاختبارات - فإن المتحول "نجا"، وهذا جرس إنذار.

\n\n

تشبيه بسيط: أنت تختبر مدى جودة نظام الإنذار الخاص بك في اكتشاف اللصوص. تضع "لصًا" (متحولًا) وترى ما إذا كان الإنذار (الاختبار) سيعمل. إذا غادر اللص بهدوء - فإن نظام الإنذار لا قيمة له.

\n\n

لماذا تغطية الكود (code coverage) مؤشر سيء؟

\n\n

تخيل هذا الكود:

\n\n
public boolean isAdult(int age) {\n    if (age >= 18) {\n        return true;\n    }\n    return false;\n}
\n\n

والاختبار:

\n\n
@Test\nvoid testIsAdult() {\n    assertTrue(isAdult(18));\n}
\n\n

ستظهر تغطية الكود 100% (هل تم تنفيذ فرعي if-else؟). لا! تم تنفيذ الفرع true فقط. لكن الاختبار ناجح. والآن تخيل أن شخصًا ما غير الشرط بالخطأ إلى age > 21. سيظل الاختبار ناجحًا، لأن 18 > 21 هي false، بينما assertTrue تتوقع true. سينتقل الخطأ إلى الإنتاج.

\n\n

الاختبار الطفري سيكتشف هذا الموقف فورًا، عن طريق إنشاء متحول يغير >= إلى > أو يعكس الشرط.

\n\n

كيف يعمل الاختبار الطفري: النظرية ببساطة

\n\n

تتكون العملية من ثلاث مراحل:

\n\n
    \n
  1. توليد المتحولين. تقوم الأداة (مثل PIT) بإنشاء نسخ متعددة من كودك مع تغييرات طفيفة: تغيير العوامل، إزالة استدعاءات الدوال، عكس الشروط.
  2. \n
  3. تشغيل الاختبارات. لكل متحول، يتم تشغيل اختبارات الوحدة الخاصة بك.
  4. \n
  5. تحليل النتائج. إذا فشل الاختبار - تم قتل المتحول. إذا نجح - فهو حي. النسبة المئوية للمتحولين المقتولين تسمى mutation score (درجة الطفرات).
  6. \n
\n\n

مؤشر جيد: 80-90% من المتحولين مقتولين. أي شيء أقل من ذلك هو سبب لإعادة كتابة الاختبارات.

\n\n

مثال عملي على Java باستخدام PIT

\n\n

لنأخذ مثالًا حقيقيًا. لنفترض أن لدينا خدمة لحساب الخصم:

\n\n
public class DiscountService {\n    public double calculateDiscount(double amount, boolean isPremium) {\n        double discount = 0.0;\n\n        if (amount > 100) {\n            discount = 0.1; // خصم 10%\n        }\n\n        if (isPremium) {\n            discount += 0.05; // إضافي 5%\n        }\n\n        // الخصم لا يمكن أن يتجاوز 30%\n        if (discount > 0.3) {\n            discount = 0.3;\n        }\n\n        return discount;\n    }\n}
\n\n

لنكتب اختبارًا ساذجًا:

\n\n
import org.junit.jupiter.api.Test;\nimport static org.junit.jupiter.api.Assertions.*;\n\nclass DiscountServiceTest {\n\n    private final DiscountService service = new                

المدونات

توصيات الكتب