اختبار الطفرات في C++: كيف تجعل الكود غير قابل للكسر

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

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

ابدأ الدورة

اختبار الطفرات في C++: عندما تكون تغطية الكود بنسبة 100% كذبة

\n\n

هل تفتخر بتغطية الكود بنسبة 95%؟ ربما تفعل ذلك دون داعٍ. تغطية الكود تشبه قفلًا يتم فحصه بمجرد هز المقبض. أما اختبار الطفرات فهو لص يحاول فعليًا فتح الخزنة. في هذه المقالة، سنشرح كيف يحول اختبار الطفرات في C++ اختباراتك من مجرد إجراء شكلي إلى سلاح حقيقي. ونعم، لن نقوم بنسخ التعريفات المملة من ويكيبيديا.

\n\n

تخيل: لقد كتبت دالة، وغطيتها بالاختبارات، وCI أخضر. ثم يحدث خطأ في الإنتاج. ألم مألوف؟ غالبًا لا تكمن المشكلة في الكود، بل في الاختبارات التي لا تتحقق مما يجب. Mutation testing لـ C++ يحل هذه المشكلة تحديدًا - فهو يُظهر مدى خطورة اختباراتك على الأخطاء.

\n\n

ما هو اختبار الطفرات ولماذا يحتاجه مطور C++؟

\n\n

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

\n\n

هذا مهم بشكل خاص لـ C++ للأسباب التالية:

\n
    \n
  • غالبًا ما يحتوي كود C++ على منطق معقد مع المؤشرات والقوالب و RAII.
  • \n
  • من السهل خداع مقاييس التغطية القياسية (line coverage, branch coverage).
  • \n
  • تكلفة الخطأ في C++ (خاصة في الأنظمة المدمجة والمالية ومحركات الألعاب) قد تكون كارثية.
  • \n
\n\n

مثال بسيط: لديك دالة للتحقق من العمر:

\n\n
bool is_adult(int age) {\n    return age >= 18;\n}
\n\n

الاختبار:

\n\n
TEST(AdultTest, Positive) {\n    EXPECT_TRUE(is_adult(20));\n}
\n\n

التغطية: 100% من الأسطر. لكن الطفرة age >= 18 → age > 18 لن يتم اكتشافها، لأن 20 لا تزال تمر. الطفرة نجت. أما لو كان لديك اختبار لـ is_adult(18) و is_adult(17) - لكنت قد التقطت الخطأ. اختبار الطفرات في C++ يدفعك لكتابة مثل هذه الاختبارات الحدودية.

\n\n

عوامل الطفرات الرئيسية في C++

\n\n

تطبق أدوات اختبار الطفرات مجموعة من العوامل (mutators). إليك الأكثر شيوعًا لـ C++:

\n\n
    \n
  • استبدال العوامل الحسابية: + → -, * → /
  • \n
  • استبدال العوامل المنطقية: && → ||, == → !=
  • \n
  • تغيير الشروط الحدودية: >= → >, < → <=
  • \n
  • حذف التعبيرات: استبدال استدعاء دالة بثابت
  • \n
  • تغيير القيمة المرجعة: return true → return false
  • \n
  • طفرة الحلقات: تغيير شرط الخروج أو الخطوة
  • \n
\n\n

مثال مع كود حقيقي:

\n\n
int factorial(int n) {\n    if (n <= 1) return 1;\n    return n * factorial(n - 1);\n}
\n\n

الطفرة #1: n <= 1 → n < 1. إذا استدعى الاختبار factorial(1) وتوقع 1، لكن الطفرة تزيل الحالة الأساسية - سيفشل الاختبار فقط إذا كنت تتحقق من factorial(0) أو factorial(1) بشكل منفصل.

\n\n

الطفرة #2: return n * factorial(n - 1) → return n + factorial(n - 1). إذا كانت الاختبارات فقط على الأرقام الموجبة، فقد لا تلاحظ استبدال الضرب بالجمع.

\n\n

هذه هي الحالات التي يكشفها mutation testing في C++.

\n\n

أدوات اختبار الطفرات في C++

\n\n

ليست كل الأدوات مفيدة بنفس القدر. إليك ثلاثة لاعبين رئيسيين في سوق C++:

\n\n

1. Mull

\n

مشروع حديث ونشط. يعمل بناءً على LLVM pass. يدعم C++17/20، ويتكامل مع Google Test و Catch2 و doctest

المدونات

توصيات الكتب