هل تخدعك اختباراتك؟ حان وقت اختبار التحور (Mutation Testing)
\n\nتكتب اختبارات الوحدة (unit tests)، وتغطية الكود (code coverage) تُبهر عينيك — 85%، 90%، ومع ذلك تظهر الأخطاء في بيئة الإنتاج (production)؟ ألم مألوف. مقياس التغطية التقليدي (line/branch coverage) هو في جوهره قياس لأي أسطر الكود تم تشغيلها. لكنه لا يخبرك بشيء عن مدى جودة اختباراتك في التحقق من المنطق البرمجي. قد تكون الاختبارات ببساطة "تمر" على الأسطر دون التحقق من صحة النتيجة.
\n\nهنا يأتي دور اختبار التحور (mutation testing). إنه بمثابة حقنة لقاح لكودك: نقوم عمدًا بإدخال "فيروسات" صغيرة (تحورات) في البرنامج، ونرى ما إذا كانت اختباراتنا تقتلها. إذا لم يفشل الاختبار بعد تغيير === إلى !== — فإن اختباراتك ضعيفة. في هذه المقالة على pythonlib.ru، سنشرح كيفية دمج اختبار التحور في مشروع JavaScript لجعل كودك موثوقًا حقًا.
ما هو اختبار التحور (Mutation Testing) بلغة بسيطة
\n\nتخيل أنك مهندس ضمان جودة (QA) يقوم بتغيير سطر في كود المطور سرًا:
\n\n// كان\nif (user.age >= 18) { allowAccess(); }\n\n// أصبح (تحور)\nif (user.age > 18) { allowAccess(); }\n\nإذا كانت اختبارات الوحدة لا تزال ناجحة (خضراء) بعد هذا التغيير — فإن الاختبارات لا تتحقق من الحالة الحدية 18. يقوم إطار عمل التحور (mutation framework) تلقائيًا بآلاف من هذه التغييرات ويُصدر تقريرًا: كم عدد التحورات التي نجت (survived). كلما قل عدد المتحورات الناجية، زادت موثوقية اختباراتك.
الأنواع الرئيسية للتحورات في JavaScript:
\n- \n
- تغيير عوامل المقارنة:
>←>=،===←!==\n - حذف استدعاءات الدوال:
validate()← سطر فارغ \n - تغيير القيم الرقمية:
0←1،100←0\n - عكس الشروط المنطقية:
if (isActive)←if (!isActive)\n - استبدال القيم النصية (أقل شيوعًا، لكنه يحدث) \n
لماذا تغطية الكود (Code Coverage) هي أمان زائف
\n\nينخدع العديد من مطوري JavaScript بتغطية كود بنسبة 100%. دعنا ننظر إلى مثال واقعي:
\n\n// دالة تحسب الخصم\nfunction getDiscount(price, isPremium) {\n if (price > 100 && isPremium) {\n return price * 0.8;\n }\n return price;\n}\n\nواختبار "مثالي":
\n\ntest('يجب إرجاع السعر للمستخدم غير المميز', () => {\n expect(getDiscount(50, false)).toBe(50);\n});\n\ntest('يجب إرجاع السعر للمستخدم المميز بسعر منخفض', () => {\n expect(getDiscount(50, true)).toBe(50);\n});\n\nالتغطية: 100% أسطر، 100% فروع. لكن التحور price > 100 ← price >= 100 سينجو! الاختبارات لا تتحقق من القيمة الحدية 100.01 أو 100 بالضبط. اختبار التحور (Mutation testing) سيكشف هذه "النقطة العمياء" فورًا.
الأداة رقم 1: Stryker Mutator لجافا سكريبت
\n\nالأداة الأكثر شيوعًا ونضجًا لاختبار التحور في عالم JS هي Stryker Mutator (أو ببساطة Stryker). يدعم Jest وMocha وJasmine وKarma ويعمل مع TypeScript.
\n\nالتثبيت والبدء السريع
\n\nقم بتثبيت Stryker بشكل عام أو محلي في مشروعك:
\n\nnpm install --save-dev @stryker-mutator/core @stryker-mutator/jest-runner\n\nأنشئ ملف الإعدادات stryker.conf.json:
{\n "$schema": "./node_modules/@stryker-mutator/core/schema/stryker-schema.json",\n "packageManager": "npm",\n "reporters": ["html", "clear-text", "progress"],\n "testRunner": "jest",\n "coverageAnalysis": "perTest",\n "thresholds": {\n "high": 80,\n "low": 60,\n "break": 50\n }\n}\n\nقم بتشغيل اختبار التحور