مستوى المقال: محترف. يفترض إنك مرتاح مع Jest أو Vitest، عارف الـ async في JavaScript، ومتعامل قبل كده مع unit testing بشكل جدّي.
لو كاتب 250 unit test على API الـ payments بتاعك، وكلها خضراء، وفي يوم 12 جالك ticket إن العميل اتخصم -0.01 جنيه، الـ tests مش غلطانة. الـ tests كانت بتفحص المدخلات اللي انت فكّرت فيها بس. Property-Based Testing بيولّد 10,000 مدخل عشوائي على كل run ويكشف الـ edge cases اللي مفيش بشر هيفكّر فيها.
Property-Based Testing بـ fast-check: ابحث عن الـ bug بدل ما تستنّى منه
المشكلة باختصار
الـ unit testing التقليدي مبني على ثلاث خطوات: arrange، act، assert. انت بتكتب مدخل (مثلاً [100, 50, 200]) ونتيجة متوقّعة ([50, 100, 200]) وبتشيك إن الدالة بترجّع النتيجة دي. المشكلة: المدخلات بتيجي من خيالك، ومخك بياخد path واحد. Property Testing بيختار 10,000 مدخل عشوائي بناءً على مواصفات الـ generators (integer موجب، array طوله بين 1 و 1000، إلخ) ويفحص property — قاعدة لازم تفضل صحيحة على أي مدخل صالح.
المثال للمبتدئ — المختبِر الذي يجرّب آلاف الزجاجات
تخيّل عندك مصنع زيت وعايز تتأكد إن غطاء الزجاجة بيقفل صح. الطريقة العادية: تاخد 5 زجاجات شكل مختلف وتجرّب الغطاء عليهم. لو بيقفل، تقول "تمام". المشكلة؟ في الإنتاج هتعدّي 50 ألف زجاجة في الشهر، فيهم 12 شكل عنق مختلف، 3 مستويات لزوجة زيت، و 4 درجات حرارة موسمية. اللي بيعمله Property Testing هو إنه بيجيب 10,000 توليفة عشوائية للـ (شكل + لزوجة + حرارة) ويختبر "هل الغطاء بيقفل بدون تسريب؟" على كل واحدة. أول ما يلاقي توليفة فيها مشكلة، بيعرضها بالظبط — ودي العبقرية اسمها shrinking: بيلاقي أصغر مثال يكسر القاعدة عشان تقدر تصحّح بسرعة.
التعريف العلمي
Property-Based Testing نشأ سنة 2000 في ورقة Koen Claessen و John Hughes "QuickCheck: A Lightweight Tool for Random Testing of Haskell Programs" المنشورة في ICFP 2000. الفكرة الأساسية: بدل ما تكتب أمثلة محددة، اكتب property — قاعدة رياضية لازم تفضل صحيحة. مثال: "لو فرّزت array وقلبتها، أول عنصر في المقلوبة لازم يساوي آخر عنصر في الأصل". الـ runner بيولّد inputs بناءً على generators (نوع fc.integer() أو fc.array(fc.integer()))، يفحص الـ property على كل مدخل، ولو فشل، بيعمل shrinking يقلّل المدخل لأصغر شكل لسه بيكسر القاعدة.
المثال التنفيذي — fast-check على دالة calculateDiscount
عندنا دالة JavaScript بتحسب الخصم على فاتورة. الـ business rule: final = price - (price * discountPercent / 100) لازم يكون بين 0 وقيمة الفاتورة، ومينفعش يطلع سالب. unit tests الموجودة عند الفريق بتفحص 8 حالات يدوية. Property test واحد بـ fast-check بيكشف bug خفي في 1.4 ثانية: