لماذا تغطية الكود (code coverage) هي فخ، واختبار التحور (mutation testing) هو الخلاص
\n\nتخيل موقفًا: مشروعك بلغة Kotlin مغطى بالاختبارات بنسبة 95%. CI أخضر، المدير سعيد، الكود يذهب إلى الإنتاج. وبعد أسبوع - خطأ في الإنتاج. هل هذا مألوف؟ على الأرجح، اختباراتك ببساطة تُنفَّذ، لكنها لا تتحقق من المنطق. هنا يأتي دور اختبار التحور (mutation testing) - منهجية لا تسمح للاختبارات بـ"التساهل".
\n\nفي هذه المقالة، سنشرح ما هو اختبار التحور في Kotlin، وكيف يعمل، وكيفية دمجه في مشروعك باستخدام الأداة الشائعة Pitest. ستتعرف على لماذا تغطية الكود بنسبة 100% ليست ضمانًا للجودة، وكيف يساعد اختبار التحور في كتابة اختبارات موثوقة حقًا.
\n\nما هو اختبار التحور؟ أشرحه باستخدام القطط
\n\nلنفترض أنك كتبت دالة تحدد ما إذا كان بإمكان القط المرور عبر الباب:
\n\nfun canCatFitThroughDoor(catWidth: Int, doorWidth: Int): Boolean {\n return catWidth <= doorWidth\n}\n\nكتبت اختبارًا:
\n\n@Test\nfun `قط بعرض 30 يمر عبر باب بعرض 40`() {\n assertTrue(canCatFitThroughDoor(30, 40))\n}\n\nالتغطية - 100%. ولكن ماذا لو قام شخص ما بتغيير <= إلى < عن طريق الخطأ؟ سيظل الاختبار ناجحًا! يقوم اختبار التحور تلقائيًا بإدخال مثل هذه "الأعطال الصغيرة" (التحورات) في الكود ويتحقق مما إذا كان اختبار واحد على الأقل سيفشل. إذا لم يفشل الاختبار - فإن اختباراتك لا تكتشف هذا الخطأ. هذا هو اختبار التحور في Kotlin.
كيف يعمل اختبار التحور: من العوامل إلى "الطفرات الناجية"
\n\nتقوم الأداة (مثل Pitest لـ Kotlin) بإنشاء طفرات - إصدارات من كودك مع تغيير واحد. إليك التحورات النموذجية:
\n\n- \n
- تغيير عوامل المقارنة:
>→>=,==→!=\n - عكس الشروط:
if (a > b)→if (a <= b)\n - إزالة استدعاءات الدوال:
someMethod()→ فارغ \n - استبدال القيم المُرجعة:
true→false\n - تغيير الأرقام:
10→11أو0\n
بعد إنشاء الطفرات، يتم تشغيل جميع اختباراتك. تنقسم النتائج إلى ثلاث فئات:
\n\n- \n
- الطفرات المقتولة (Killed) - فشلت الاختبارات، تم اكتشاف التحور. ممتاز! \n
- الطفرات الناجية (Survived) - نجحت الاختبارات على الرغم من تغيير الكود. إشارة تحذيرية. \n
- مغطاة ولكن غير مقتولة (Covered but not killed) - تم تشغيل الاختبارات ولكنها لم تلاحظ التغيير. \n
هدفك هو تقليل عدد الطفرات الناجية قدر الإمكان (من الناحية المثالية - 0). هذا هو اختبار التحور لكود Kotlin عمليًا.
\n\nPitest: الأداة الرئيسية لـ Kotlin
\n\nبالنسبة للغات JVM (Java, Kotlin)، المعيار الفعلي هو Pitest (PIT Mutation Testing). وهو يدعم Kotlin من خلال الإضافة pitest-kotlin. إليك كيفية توصيله في Gradle:
plugins {\n id("info.solidsoft.pitest") version "1.15.0"\n}\n\npitest {\n targetClasses.set(listOf("com.myapp.*")) // الفئات التي سيتم تحويرها\n targetTests.set(listOf("com.myapp.*")) // الاختبارات التي سيتم تشغيلها\n threads.set(4) // خيوط متوازية\n outputFormats.set(listOf("XML", "HTML")) // التقارير\n mutationThreshold.set(80) // الحد الأدنى لنسبة المقتولة\n timeoutConstInMillis.set(10000) // مهلة الاختبار\n}\n\ndependencies {\n testImplementation("org.pitest:pitest-kotlin:1.15.0")\n}\n\nالتشغيل بأمر واحد:
\n\n./gradlew pitest\n\nبعد التنفيذ، في build/reports/pites