الاختبار القائم على الخصائص في PHP: كسر الكود أسرع من كتابته

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

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

ابدأ الدورة

مقدمة: لماذا تخدعك اختباراتك وأنت تصدقها

\n\n

تخيل: لقد كتبت 50 اختبار وحدة، غطيت كل if-else، شغّلتها عبر CI — كلها خضراء. بعد أسبوع، ينهار الإنتاج مع خطأ "القسمة على صفر". كيف؟ أليست الاختبارات قد نجحت؟ مألوف؟

\n\n

مشكلة اختبارات الوحدة التقليدية (التقريبية، بأرقام محددة) هي أنها تتحقق من ما تعرفه بالفعل. أنت تختار الوسائط التي، في رأيك، ستؤدي إلى خطأ. لكن عقل المبرمج يمكن التنبؤ به: كلنا نكتب testDivideByZero وننسى testDivideByNegativeZero أو testDivideByVeryLargeFloat.

\n\n

هنا يظهر على المسرح الاختبار القائم على الخصائص في PHP — وهي طريقة لا تتحقق من السيناريوهات المحددة، بل تثبت خصائص الكود الخاص بك لآلاف من المدخلات العشوائية. اليوم سنشرح كيفية تطبيق هذا النهج في مشروع PHP الخاص بك باستخدام Eris و PHPUnit. ستتعلم كيفية العثور على الأخطاء التي تفوتها الاختبارات العادية، ولماذا هذا ليس سحرًا بل هندسة.

\n\n

ما هو الاختبار القائم على الخصائص؟ (ولماذا ليس عشوائيًا)

\n\n

اختبار الوحدة العادي يبدو هكذا: assert(2 + 2 === 4). أنت تتحقق من عبارة واحدة محددة. الاختبار القائم على الخصائص (PBT) يعمل بشكل مختلف: أنت تصوغ خاصية — عبارة يجب أن تكون صحيحة لـ أي مدخلات صحيحة.

\n\n

مثال على خاصية لدالة الجمع:

\n
    \n
  • التبديلية: a + b === b + a لأي أرقام.
  • \n
  • الاستقرارية: التطبيق المتكرر للدالة لا يغير النتيجة.
  • \n
  • الثبات: النتيجة دائمًا موجبة (إذا كانت هذه خاصية).
  • \n
\n\n

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

\n\n

في PHP، هذه التقنية مفيدة بشكل خاص بسبب الكتابة الديناميكية: أنت لا تعرف أبدًا أي نوع سيصل إلى الدالة في الواقع.

\n\n

أدوات الاختبار القائم على الخصائص في PHP

\n\n

الأداة الرئيسية هي مكتبة Eris (github.com/giorgiosironi/eris). تتكامل مع PHPUnit وتوفر مولدات بيانات عشوائية. البديل هو الإمكانيات المدمجة في PHPUnit 10+ (لكنها لا تزال غير ناضجة). سنستخدم Eris لأنها أداة ناضجة وموثقة.

\n\n

التثبيت عبر Composer:

\n
composer require --dev giorgiosironi/eris
\n\n

سيكون Faker مفيدًا أيضًا لتوليد الهياكل المعقدة (البريد الإلكتروني، الأسماء، العناوين).

\n\n
composer require --dev fakerphp/faker
\n\n

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

\n\n

لنفترض أن لدينا دالة sortArray() تستخدم quicksort. لنكتب خاصية: "يجب ألا يحتوي المصفوفة بعد الترتيب على عناصر أصغر من العنصر السابق".

\n\n
use Eris\\TestTrait;\nuse Eris\\Generator\\IntegerGenerator;\nuse PHPUnit\\Framework\\TestCase;\n\nclass SortTest extends TestCase\n{\n    use TestTrait;\n\n    public function testSortedArrayProperty(): void\n    {\n        $this->forAll(\n            Generator\\seq(Generator\\int())\n        )->then(function (array $input) {\n            $sorted = sortArray($input);\n            for ($i = 0; $i < count($sorted) - 1; $i++) {\n                $this->assertLessThanOrEqual(\n                    $sorted[$i + 1],\n                    $sorted[$i],\n                    "المصفوفة غير مرتبة: " . json_encode($sorted)\n                );\n            }\n        });\n    }\n}\n
\n\n

تشغيل

المدونات

توصيات الكتب