الاختبار القائم على الخصائص في C++: اكتشف الأخطاء التي لم تبحث عنها

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

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

ابدأ الدورة

ماذا لو كانت اختبارات الوحدة الخاصة بك كاذبة؟

\n

لقد كتبت مئة اختبار وحدة. إنها ناجحة. يبدو كود C++ مثالياً. ولكن عند تشغيل التطبيق في بيئة الإنتاج، يتعطل مع خطأ تجزئة (segmentation fault) في الطلب رقم 100500. هل هذا الألم مألوف؟

\n

اختبارات الوحدة التقليدية تتحقق فقط مما توصلت إليه أنت بنفسك. إنها عمياء تجاه السيناريوهات التي لم تخطر ببالك. الاختبار القائم على الخصائص (أو property-based testing، PBT) يحل هذه المشكلة بشكل جذري: فهو لا يتحقق من أمثلة محددة، بل يثبت خصائص (properties) الكود الخاص بك لـ ملايين من المدخلات العشوائية.

\n

في هذه المقالة، سنشرح كيفية تطبيق الاختبار القائم على الخصائص في C++ باستخدام مكتبة RapidCheck، وسنرى كيف يكتشف الأخطاء التي تفوتها اختبارات الوحدة العادية. هل أنت مستعد لجعل الكود الخاص بك يعاني حقاً؟ هيا بنا.

\n\n

الاختبار القائم على الخصائص: الفلسفة في 30 ثانية

\n

تخيل أنك تختبر دالة فرز. اختبار عادي: sort([3,1,2]) == [1,2,3]. حسناً، تحققنا من حالة واحدة.

\n

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

\n

الفكرة الأساسية: أنت تصف الثوابت (properties) التي يجب أن تتحقق دائماً، وليس مدخلات ومخرجات محددة.

\n\n

متى يكون PBT مفيداً بشكل خاص في C++؟

\n
    \n
  • الخوارزميات وهياكل البيانات (الفرز، البحث، الرسوم البيانية)
  • \n
  • المحللات (Parsers) وأدوات التسلسل (Serializers) (التشفير/فك التشفير)
  • \n
  • الدوال الرياضية (العمليات العكسية، نطاقات القيم)
  • \n
  • التعامل مع الذاكرة (تسريبات، تحرير مزدوج)
  • \n
  • أي كود يحتوي على ثوابت واضحة
  • \n
\n\n

RapidCheck: صديقك الجديد الأفضل

\n

توجد عدة مكتبات لـ PBT في C++، لكن RapidCheck هي الأكثر نضجاً وسهولة في الاستخدام. إنها مستوحاة من مكتبة QuickCheck الخاصة بـ Haskell وتتكامل بشكل ممتاز مع Google Test و Catch2.

\n

التثبيت بسيط: إنها مكتبة header-only، يكفي نسخ الملفات من include/rapidcheck إلى مشروعك. أو استخدم Conan/vcpkg.

\n\n

الاختبار الأول: خاصية العملية العكسية

\n

لنفترض أن لدينا دالة تقوم بتشفير سلسلة نصية إلى base64 وفك تشفيرها مرة أخرى. ما الخاصية الواضحة؟ "فك التشفير بعد التشفير يعيد السلسلة النصية الأصلية".

\n
#include <rapidcheck.h>\n#include <string>\n\nstd::string encode_base64(const std::string& input);\nstd::string decode_base64(const std::string& input);\n\nint main() {\n    rc::check("فك التشفير بعد التشفير يعيد النص الأصلي",\n              [](const std::string& original) {\n                  // مولد السلسلة النصية مدمج بالفعل\n                  auto encoded = encode_base64(original);\n                  auto decoded = decode_base64(encoded);\n                  RC_ASSERT(decoded == original);\n              });\n    return 0;\n}\n
\n

ماذا يحدث هنا؟ يقوم RapidCheck بتوليد سلاسل نصية عشوائية (بما في ذلك الفارغة، والتي تحتوي على Unicode، والبايتات الصفرية). لكل منها، يتحقق من الخاصية. إذا وجد مثالاً مضاداً، فإنه يقوم بتصغيره (shrinking) إلى أصغر مثال لا يزال يكسر الاختبار. هذا ذهب: أنت لا تحصل فقط على "يوجد خطأ"، بل على حالة محددة قابلة للتكرار.

\n\n

خصائص متقدمة: أكثر من مجرد مساواة

\n

الرتابة (Monotonicity) والثبات (Idempotence)

\n

بالنسبة لدالة remove_duplicates:

\n
    \n
  • الثبات (Idempotence): إذا تم تطبيقها مرتين، لا تتغير النتيجة.
  • \n
  • الترتيب غير مهم: القائمة الناتجة تحتوي على نفس عناصر القائمة الأصلية، ولكن بدون تكرار.
  • \n
\n
#include <vector>\n#include <algo                

المدونات

توصيات الكتب