ماذا لو كانت الاختبارات تكتب نفسها؟ تعرّف على الاختبار القائم على الخصائص
\n\nهل سبق لك أن كتبت اختبار وحدة، وشغّلته، وحصلت على علامة خضراء، ثم بعد شهر وجدت خطأ في نفس الدالة؟ مألوف؟ اختبارات الوحدة التقليدية تشبه التحقق من أن محفظتك ليست فارغة من خلال النظر إلى ورقة نقدية واحدة فقط. أنت تتحقق من سيناريو واحد محدد، ومجموعة واحدة من بيانات الإدخال. ماذا لو أخبرتك أن هناك طريقة لجعل الكمبيوتر يبتكر بنفسه آلاف السيناريوهات المختلفة ويتحقق مما إذا كان كودك سينهار؟
\n\nهذا ليس خيالاً. هذا هو الاختبار القائم على الخصائص في JavaScript. بدلاً من كتابة "إذا أدخلت 2 و 3، سأحصل على 5"، فإنك تصف خاصية لدالتك: "لأي رقمين، نتيجة الجمع ستكون رقماً أكبر أو يساوي كل من الرقمين". وبيئة الاختبار تولد مئات الأزواج العشوائية من الأرقام، وتتحقق من هذا الادعاء.
\n\nفي هذه المقالة، سنشرح كيف يعمل الاختبار القائم على الخصائص، وكيف يختلف عن الاختبارات العادية، وكيفية دمجه في مشروعك باستخدام المكتبة الرائعة fast-check. سترى كيف يسهل العثور على الأخطاء "الحدودية" التي لم تكن لتفكر في البحث عنها.
لماذا لا تنقذك اختبارات الوحدة العادية دائماً؟
\n\nدعنا نذهب مباشرة إلى الموضوع. لنفترض أن لديك دالة تقوم بترتيب مصفوفة أرقام تصاعدياً:
\n\nfunction sortNumbers(arr) {\n return arr.slice().sort((a, b) => a - b);\n}\n\nتكتب اختبار وحدة:
\n\ntest('ترتيب المصفوفة [3, 1, 2]', () => {\n expect(sortNumbers([3, 1, 2])).toEqual([1, 2, 3]);\n});\n\nالاختبار أخضر. أنت سعيد. ولكن ماذا لو كانت المصفوفة تحتوي على null؟ أو undefined؟ أو أرقام كبيرة جداً؟ أو مصفوفة طولها 10000 عنصر؟ لن تكتب اختباراً لكل حالة من هذه الحالات. أما الاختبار القائم على الخصائص في JavaScript فسيفعل.
بدلاً من سرد الأمثلة، تصف الثوابت (الخصائص) التي يجب أن تكون صحيحة دائماً:
\n- \n
- طول المصفوفة المرتبة يساوي طول المصفوفة الأصلية. \n
- كل عنصر من المصفوفة الأصلية موجود في المصفوفة المرتبة. \n
- لأي فهرس
i، يكونarr[i] <= arr[i+1]صحيحاً. \n
هذه هي الخصائص التي سيتحقق منها اختبار الخصائص.
\n\nمكتبة fast-check: مساعدك في الاختبار العشوائي
\n\nفي عالم JavaScript، الأداة الرئيسية للاختبار القائم على الخصائص هي fast-check. يمكنها توليد بيانات عشوائية من أي نوع: أرقام، نصوص، مصفوفات، كائنات، تواريخ - وحتى هياكل معقدة.
\n\nالتثبيت بسيط:
\nnpm install fast-check --save-dev\n\nالآن لنكتب أول اختبار قائم على الخصائص لدالة sortNumbers. سنستخدم إطار Jest، لكن fast-check يعمل مع أي مشغل اختبار.
const fc = require('fast-check');\n\ntest('الترتيب يحافظ على طول المصفوفة', () => {\n fc.assert(\n fc.property(fc.array(fc.integer()), (arr) => {\n const sorted = sortNumbers(arr);\n return sorted.length === arr.length;\n })\n );\n});\n\nماذا يحدث هنا؟ fc.array(fc.integer()) هو مولد ينشئ مصفوفات عشوائية من أعداد صحيحة عشوائية. fc.property يأخذ هذا المولد ويتحقق من الادعاء (دالتنا السهمية) على 100 (افتراضياً) مجموعة بيانات مختلفة. إذا فشل اختبار واحد على الأقل - سيرمي fc.assert خطأ ويعرض مثالاً مضاداً.
إضافة المزيد من الخصائص
\n\nخاصية واحدة جيدة، لكن ثلاث خصائص أفضل. دعنا نتحقق من أن المصفوفة بعد الترتيب مرتبة بالفعل:
\n\ntest('الترتيب يعيد مصفوفة مرتبة', () =>