اختبار الطفرات في PHP: كيف تكتب اختبارات تكتشف الأخطاء فعلياً

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

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

ابدأ الدورة

مقدمة: لماذا تغطية الكود بنسبة 100% مجرد وهم؟

\n

هل شعرت يوماً بالفخر عندما رأيت تقرير PHPUnit يظهر "Code Coverage: 98%"؟ ثم بعد إطلاق التحديث، يظهر خطأ كارثي في بيئة الإنتاج؟ مألوف، أليس كذلك؟ المشكلة أن التغطية التقليدية (line/statement coverage) تقيس فقط أي سطور من الكود تم تنفيذها. إنها لا تتحقق مما إذا كانت اختباراتك تختبر المنطق الفعلي.

\n

تخيل أنك كتبت اختباراً يتحقق من أن دالة ما تُرجع true. والدالة نفسها تُرجع true دائماً، حتى لو تم كسرها. سينجح الاختبار، وستكون التغطية 100%، لكنك لن تكتشف الخطأ. هنا يأتي دور اختبار الطفرات في PHP — تقنية تقلب المفاهيم التقليدية عن الاختبارات.

\n

في هذه المقالة، سنشرح ما هو اختبار الطفرات، وكيف يعمل مع أمثلة حقيقية من كود PHP، وكيفية تطبيقه في مشروعك باستخدام مكتبة Infection.

\n\n

ما هو اختبار الطفرات؟ (وليس سحراً)

\n

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

\n

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

\n

في PHP، الأداة الرئيسية لذلك هي Infection — مكتبة تُشغل PHPUnit على كل طفرة وتحلل النتائج.

\n\n

كيف يبدو ذلك عملياً؟

\n

لنفترض أن لديك كلاس آلة حاسبة بسيط:

\n
<?php\nclass Calculator\n{\n    public function add(int $a, int $b): int\n    {\n        return $a + $b;\n    }\n}
\n

واختبار له:

\n
<?php\nuse PHPUnit\\Framework\\TestCase;\n\nclass CalculatorTest extends TestCase\n{\n    public function testAdd()\n    {\n        $calc = new Calculator();\n        $result = $calc->add(2, 3);\n        $this->assertEquals(5, $result);\n    }\n}
\n

ستُنشئ أداة Infection طفرة، وتستبدل + بـ -:

\n
return $a - $b;
\n

ثم تُشغل الاختبار. إذا فشل الاختبار، قُتلت الطفرة. إذا نجح الاختبار (وسينجح لأنك تختبر فقط 2+3، وليس 2-3) — نجت الطفرة. هذه إشارة: اختبارك ليس جيداً بما يكفي.

\n\n

تثبيت Infection والتشغيل الأول

\n

أولاً، نثبّت Infection عبر Composer:

\n
composer require --dev infection/infection
\n

ثم ننشئ ملف الإعدادات infection.json (أو شغّل vendor/bin/infection للإعداد التفاعلي).

\n

مثال على إعدادات بسيطة:

\n
{\n    "source": {\n        "directories": [\n            "src"\n        ]\n    },\n    "phpUnit": {\n        "configDir": "."\n    },\n    "mutators": {\n        "@default": true\n    }\n}
\n

نُشغل اختبار الطفرات:

\n
vendor/bin/infection --min-msi=100
\n

حيث --min-msi=100 يعني أننا نريد قتل 100% من الطفرات (مثالي، لكن نادراً ما يتحقق). سينشئ Infection تقريراً بصيغة HTML (مجلد infection.html)، حيث سيتم تمييز الطفرات الناجية باللون الأحمر.

\n\n

أنواع الطفرات في PHP (مع أمثلة)

\n

يدعم Infection عشرات المُطفرات. إليك الأكثر شيوعاً:

\n\n

1. العوامل الرياضية

\n

استبدال + بـ -، * بـ /، % بـ *، إلخ.

\n
// الكود الأصلي\nreturn $price * $quantity;\n\n// الطفرة\nreturn $price / $quantity;

المدونات

توصيات الكتب