أساسيات JUnit 5
أساسيات JUnit 5
JUnit 5 هو إطار الاختبار المعياري الفعلي على JVM. إنه ليس مكتبة واحدة بل بنية تتكون من ثلاثة وحدات: JUnit Platform (منصة الإطلاق وواجهة برمجة المحركات)، وJUnit Jupiter (نموذج البرمجة الجديد والتعليقات التوضيحية التي تكتبها يوميًا)، وJUnit Vintage (المشغّل المتوافق مع الإصدار السابق لاختبارات JUnit 3/4). في العمل اليومي تتعامل بشكل شبه حصري مع Jupiter.
إضافة JUnit 5 إلى مشروعك
تتضمن أدوات البناء الحديثة JUnit 5 تلقائيًا عند إنشاء مشروع، لكن من المفيد معرفة الاعتماديات الصريحة. في Maven أضف BOM واعتمادية واحدة بنطاق test:
مع Gradle (Kotlin DSL):
التعليق التوضيحي @Test
يُعدّ @Test العلامة الأساسية التي تحوّل دالة عادية إلى حالة اختبار. في Jupiter يقع في حزمة org.junit.jupiter.api — وهي حزمة مختلفة عن org.junit.Test القديمة في JUnit 4، لذلك إذا استوردت بيئة التطوير الحزمة الخاطئة سيتجاهل مشغّل الاختبارات الدالة بصمت.
قواعد دالة @Test الصالحة:
- يجب أن تكون الكلاس غير مجردة (non-abstract) وأن يكون لها مُنشئ واحد يمكن الوصول إليه (أو لا شيء مما يُشغّل المُنشئ الافتراضي).
- يجب أن تكون الدالة غير خاصة (non-private) وأن تُعيد void. لا يشترط JUnit 5
public— والمستوى الافتراضي (package-private) هو الأسلوب المفضّل. - يجب ألا تأخذ الدالة أي معاملات إلا إذا كنت تستخدم محلّلات المعاملات (مُغطّاة في دروس لاحقة).
أول اختبار
فيما يلي كلاس مكتفية بذاتها تختبر دالة مساعدة صغيرة:
reverseOfEmptyStringIsEmpty() يخبرك فورًا بما سيفشل في حال إخفاق الاختبار. أسماء مثل test1() أو testReverse() لا تفيد بشيء. تستخدم منصة JUnit اسم الدالة كاسم للعرض في التقارير، لذا تهمّ القراءة في المصدر وفي مخرجات CI.
كيف تُنفَّذ الاختبارات
فهم نموذج التنفيذ يمنع فئة كاملة من أخطاء الترتيب الخفية.
- الاكتشاف. تمسح منصة JUnit مسار الكلاسات بحثًا عن كلاسات تحتوي دوالًا موسومة بـ
@Test. بالاتفاق تقع هذه الكلاسات تحتsrc/test/javaوتعكس هيكل الحزمة للكود الإنتاجي الذي تختبره. - الإنشاء. يُنشئ JUnit 5 نسخة جديدة من كلاس الاختبار لكل دالة اختبار بشكل افتراضي. هذا القرار المتعمد يمنع تسرّب الحالة المتغيّرة المشتركة بين الاختبارات — وهو مصدر رئيسي للاختبارات المتقلبة في JUnit 4 التي كانت تُعيد استخدام نسخة واحدة لكل الكلاس.
- تنفيذ الدالة. تستدعي المنصة الدالة. إذا اكتملت دون رمي استثناء ينجح الاختبار. إذا هرب أي استثناء — بما فيه
AssertionErrorمن تأكيد فاشل — يُعلَّم الاختبار بالفشل. - إعداد التقرير. تُجمع النتائج وتُعرض بواسطة مشغّل الاختبارات في بيئة التطوير، أو Maven Surefire، أو تقرير HTML لـ Gradle.
ترتيب تنفيذ الاختبارات
لا يضمن JUnit 5 افتراضيًا الترتيب الذي تُنفَّذ به الدوال داخل كلاس. الترتيب حتمي عبر التشغيلات (استنادًا إلى تجزئة اسم الدالة)، لكنه ليس أبجديًا ولا بترتيب التصريح. هذا مقصود: الاختبارات التي تنجح فقط بترتيب معين تُخفي اعتمادية خفية وليست اختبارات وحدة حقيقية.
إذا احتجت ترتيبًا محددًا — لاختبارات التكامل أو السيناريوهات — ضع تعليقًا توضيحيًا على الكلاس بـ @TestMethodOrder(MethodOrderer.OrderAnnotation.class) ثم استخدم @Order(1)، @Order(2)، إلخ على كل دالة. احتفظ بهذا للسيناريوهات الحقيقية، لا كعكاز لاختبارات وحدة سيئة العزل.
أسماء العرض
يتيح لك التعليق التوضيحي @DisplayName استبدال اسم الدالة بسلسلة نصية مقروءة تتضمن مسافات أو أحرفًا خاصة — مفيد عندما لا يستطيع اسم الدالة التعبير الكامل عن النية:
test42() تفقد هذا التنقل. اسم دالة جيد مع @DisplayName اختياري للتقرير هو المزيج الصحيح.
تعطيل اختبار
استخدم @Disabled (مع سلسلة سبب إلزامية) عندما يجب تخطّي اختبار مؤقتًا — لا تحذفه أبدًا ولا تعلّق عليه بتعليق كود:
الاختبار المعطَّل لا يزال يظهر في التقرير كـ skipped، مما يحافظ على الوضوح بأن شيئًا ما لا يعمل عن قصد. حذف الاختبار يُزيل هذا الوضوح بصمت.
الخلاصة
Jupiter في JUnit 5 هو طبقة التعليقات التوضيحية التي تتفاعل معها يوميًا. يُعلِّم @Test دالة غير خاصة وتُعيد void وليس لها معاملات كحالة اختبار. تُنشأ نسخة جديدة من الكلاس لكل دالة مما يُزيل التلوث بين الاختبارات. تنجح الاختبارات إذا لم يهرب أي استثناء وتفشل عند أي استثناء مرمي بما فيه إخفاقات التأكيد. الأسماء الجيدة للدوال والاختياري @DisplayName تُبقي تقارير الاختبارات مقروءة. في الدرس التالي سنتعمق في واجهة برمجة التأكيدات في JUnit 5، بما فيها التأكيدات المجمّعة وتأكيدات الاستثناءات وقيود المهلة الزمنية.