JUnit اختبار المعلمات مع مثال باستخدام @Parameters
⚡ ملخص ذكي
الاختبارات ذات المعلمات في JUnit يتم تشغيل نفس طريقة الاختبار بشكل متكرر مع قيم إدخال مختلفة، بحيث تغطي طريقة واحدة العديد من السيناريوهات. توفر التعليقات التوضيحية @RunWith و @Parameters مجموعة البيانات لكل تكرار.
ما هو الاختبار المُعَلم في JUnit?
A اختبار مُعَلم هو اختبار يُنفّذ نفس أسلوب الاختبار مرارًا وتكرارًا باستخدام قيم مختلفة. يساعد هذا الاختبار المطورين على توفير الوقت عند كتابة اختبارات تختلف فقط في مدخلاتها ونتائجها المتوقعة.
باستخدام اختبار مُعَلم، يمكن إعداد طريقة اختبار تسترجع البيانات من مصدر بيانات معين. وهذا ما يجعلها أبسط أشكال الاختبار المبني على البيانات متوفر بالداخل JUnit بشكل مستقل، دون الحاجة إلى مكتبة خارجية.
لنفترض اختبارًا بسيطًا يجمع أرقامًا مختلفة. قد يبدو الكود كالتالي:
يؤدي الأسلوب المذكور أعلاه إلى الكثير من التكرار. فكل زوج جديد من الأرقام يحتاج إلى عبارة تأكيد أخرى داخل نفس الدالة، وفشل التأكيد الأول يخفي كل تأكيد يليه.
هناك حاجة إلى نهج أبسط. باستخدام اختبار مُعَلم، يمكنك إضافة دالة واحدة تُوفر عشرة مدخلات بيانات، وسيتم تشغيل اختبارك عشر مرات تلقائيًا.
خطوات إنشاء معلمة JUnit اختبار
يوضح الكود التالي مثالاً على اختبار مُعامل. يختبر هذا الكود دالة sum() الخاصة بفئة Airthematic، وهي الصيغة المستخدمة في جميع أنحاء المشروع النموذجي.
الخطوة 1) أنشئ فئة. في هذا المثال، سنقوم بإدخال رقمين باستخدام دالة sum(int, int)، والتي ستعيد مجموع الرقمين المُدخلين.
الخطوة 2) أنشئ فئة اختبار ذات معلمات.
Code تفسير
- Code خط 11: قم بتعليق فئة الاختبار الخاصة بك باستخدام @RunWith(Parameterized.class).
- Code خط 13: الإعلان عن المتغير "firstNumber" على أنه خاص واكتبه كـ int.
- Code خط 14: تعريف المتغير 'secondNumber' كمتغير خاص ونوعه int.
- Code خط 15: تعريف المتغير 'expectedResult' كمتغير خاص ونوعه int.
- Code خط 16: تعريف المتغير 'airthematic' كمتغير خاص ونوعه كـ Airthematic.
@إركض مع(اسم_الفئة.الفئة): الـ @إركض مع تُستخدم التعليقات التوضيحية لتحديد اسم فئة المُشغِّل. إذا لم نُحدد أي نوع كمعامل، فسيختار وقت التشغيل حظرJUnitعداء من الفئة الرابعة بشكل افتراضي.
تتولى هذه الفئة مسؤولية تشغيل الاختبارات باستخدام نسخة اختبار جديدة. وهي مسؤولة عن استدعاء JUnit أساليب دورة الحياة مثل الإعداد (ربط الموارد) والإنهاء (تحرير الموارد)، والتي تم وصفها في JUnit تثبيت إختبار البرنامج التعليمي.
لتعيين المعلمات، تحتاج إلى إضافة تعليق توضيحي للفئة باستخدام @RunWith وتمرير .class المطلوب لاختباره.
الخطوة 3) أنشئ دالة إنشاء تقوم بتخزين بيانات الاختبار. تقوم هذه الدالة بتخزين 3 متغيرات.
الخطوة 4) قم بإنشاء طريقة ثابتة تقوم بإنشاء وإرجاع بيانات الاختبار.
Code خط 32,33: إنشاء مصفوفة ثنائية الأبعاد (مع توفير معلمات الإدخال للجمع). باستخدام دالة asList، نقوم بتحويل البيانات إلى نوع قائمة، لأن نوع الإرجاع لدالة الإدخال هو مجموعة.
Code خط 30: باستخدام @حدود تعليق توضيحي لإنشاء مجموعة من بيانات الإدخال لإجراء اختبارنا.
تُعيد الدالة الثابتة المُحددة بواسطة التعليق التوضيحي @Parameters مجموعةً، حيث يُمثل كل عنصر فيها بيانات الإدخال لتكرار واحد من الاختبار. لنفترض العنصر {1,2,3}. هنا:
- الرقم الأول = 1
- الرقم الثاني = 2
- النتيجة المتوقعة = 3
هنا، سيتم تمرير كل عنصر من عناصر المصفوفة إلى الدالة البانية، عنصرًا تلو الآخر، نظرًا لإنشاء نسخ متعددة من الصنف. وبالتالي، فإن المصفوفات الخمس المُعلنة في المثال تُنتج عمليات التشغيل الخمس التالية:
| تكرار | الرقم الأول | الرقم الثاني | نتيجة متوقعة | خط الكونسول |
|---|---|---|---|---|
| [0] | 1 | 2 | 3 | مجموع Numbers = : 3 |
| [1] | 11 | 22 | 33 | مجموع Numbers = : 33 |
| [2] | 111 | 222 | 333 | مجموع Numbers = : 333 |
| [3] | 10 | 9 | 19 | مجموع Numbers = : 19 |
| [4] | 100 | 9 | 109 | مجموع Numbers = : 109 |
الخطوة 5) الكود الكامل.
Code التفسير:
- Code خط 25: يُستخدم التعليق التوضيحي @Before لإعداد الموارد (Airthematic.class هنا). يُستخدم التعليق التوضيحي @Before هنا لتشغيله قبل كل حالة اختبار، ويحتوي على الشروط المسبقة للاختبار.
- Code خط 36: استخدام التعليق التوضيحي @Test لإنشاء اختبارنا.
- Code خط 39: خلق بيان التأكيد للتحقق مما إذا كان مجموعنا مساوياً لما توقعناه.
الخطوة 6) أنشئ فئة تشغيل اختبار لتشغيل الاختبار ذي المعلمات:
Code التفسير:
- Code خط 8: تعريف الدالة الرئيسية للفئة Test التي ستشغل برنامجنا JUnit الاختبار.
- Code خط 9: تنفيذ حالات الاختبار باستخدام JUnitCore.runClasses، الذي يأخذ اسم فئة الاختبار كمعامل (في مثالنا نستخدم AirthematicTest.class).
- Code خط 11: معالجة النتيجة باستخدام حلقة تكرارية وطباعة النتيجة الفاشلة.
- Code خط 13: طباعة النتيجة الناجحة.
الإخراج:
هذه هي النتيجة، والتي تُظهر اختبارًا ناجحًا بدون أي فشل. tracهـ، كما هو موضح أدناه. لاحظ أن JUnit يعرض العرض إدخالًا واحدًا لكل صف بيانات بدلاً من اختبار واحد:
انظر إلى النتيجة على الشاشة، والتي تُظهر مجموع الرقمين:
الاختبارات المعيارية في JUnit 5 مع @ParameterizedTest
المثال أعلاه مكتوب لـ JUnit 4. JUnit الإصدار 5 (جوبيتر) يُلغي نموذج المُشغّل تمامًا، لذا تختفي الدالة @RunWith(Parameterized.class) ومنشئ البيانات وحقول الكائن. JUnit الكود رقم 4 الموضح أعلاه ليس قديمًا: لا يزال يعمل دون تغيير على JUnit يتم استخدام المنصة عبر المحرك القديم. ومع ذلك، عادةً ما تُكتب الاختبارات الجديدة باستخدام @ParameterizedTest.
يلزم وجود تبعيتين: junit-jupiter-api لتعليقات الاختبار و معلمات junit-jupiter بالنسبة للدعم المُعَلم. بدون العنصر الثاني، لن يتم حل التعليقات التوضيحية للمصدر على الإطلاق.
import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; class AirthematicTest { // one row per iteration, no constructor and no runner @ParameterizedTest(name = "{0} + {1} = {2}") @CsvSource({"1, 2, 3", "11, 22, 33", "111, 222, 333", "10, 9, 19", "100, 9, 109"}) void sumOfTwoNumbers(int firstNumber, int secondNumber, int expectedResult) { assertEquals(expectedResult, new Airthematic().sum(firstNumber, secondNumber)); } }
يقدم كوكب المشتري مصادر متعددة للحجج، ويعتمد المصدر الصحيح على شكل البيانات:
| شرح المصدر | لوازم | استخدمه عندما |
|---|---|---|
| @ValueSource | عمود واحد من القيم الحرفية | يأخذ الاختبار وسيطًا واحدًا فقط |
| @CsvSource | صفوف مفصولة بفواصل مضمنة | تُقرأ الجداول الصغيرة من الأرقام والسلاسل النصية بشكل واضح في الملف |
| @CsvFileSource | يتم قراءة الصفوف من ملف CSV الموجود في مسار فئة الاختبار | مجموعة البيانات كبيرة أو يتم الاحتفاظ بها خارج الكود |
| @MethodSource | مصنع ثابت يُعيد سلسلة من الوسائط | هناك حاجة إلى كائنات حقيقية أو قيم محسوبة أو بيانات عشوائية |
| @EnumSource | ثوابت التعداد | يجب اختبار كل قيمة من قيم التعداد |
هناك قاعدتان تُربكان معظم المبتدئين. أولاً، يتم تجاهل أي تعليق توضيحي للمصدر مُضاف إلى دالة @Test عادية، لذا يجب أن تحمل الدالة @ParameterizedTest. ثانياً، تُقرأ القيمة الفارغة غير المُقتبسة في @CsvSource على أنها قيمة فارغة (null)، بينما تُقرأ القيمة الفارغة المُقتبسة على أنها سلسلة نصية فارغة.
استخدم JUnit تُطبَّق أربع تعليقات توضيحية مستخدمة في هذه المقالة على Jupiter كما يلي: @RunWith(Parameterized.class) تُصبح @ParameterizedTest بالإضافة إلى تعليق توضيحي للمصدر، و@Parameters تُصبح @MethodSource أو @CsvSource، و@Before تُصبح @BeforeEach. تُغطَّى القائمة الكاملة في JUnit الشروح البرنامج التعليمي.
مزايا وقيود الاختبارات المعيارية
لا يُعدّ استخدام المعلمات مجانيًا. فهو يزيل التكرار، ولكنه يقيّد أيضًا كيفية كتابة الاختبار، لذا يجدر معرفة كلا الجانبين قبل تحويل مجموعة اختبارات موجودة.
المزايا
- Less التكرار: إحدى الطرق تستبدل مجموعة من عبارات التأكيد المتطابقة تقريبًا، كما توضح لقطة الشاشة الأولى في هذه المقالة.
- تغطية أرخص: إضافة حالة استثنائية تكلف صف بيانات إضافي بدلاً من صف بيانات جديد بالكامل حالة اختبار الأسلوب.
- تقارير دقيقة: يتم الإبلاغ عن كل تكرار على حدة، لذلك JUnit يُحدد العرض الصف الذي فشل تحديداً بدلاً من فشل واحد مُجمّع.
- البيانات المركزية: توجد المدخلات في طريقة واحدة ويمكن نقلها لاحقًا إلى ملف CSV أو مصنع دون المساس بالتأكيدات.
القيود
- أحد أشكال التأكيد: كل صف يقوم بتشغيل نفس التأكيدات، لذلك فإن السيناريو الذي يحتاج إلى عمليات تحقق مختلفة لا يزال بحاجة إلى طريقة اختبار خاصة به.
- نطاق على مستوى الفصل الدراسي في JUnit 4: يقوم المُشغِّل بتعريف الفئة بأكملها، لذا فإن طرق @Test غير ذات الصلة في تلك الفئة تعمل أيضًا مرة واحدة لكل صف.
- تقارير غير قابلة للقراءة: بدون قالب اسم، تظهر حالات الفشل على أنها testAirthematicTest[3]، وهو ما لا يقول شيئًا عن البيانات التي تعطلت.
- بيانات مضمنة ضخمة: تتسبب المصفوفات الكبيرة في إعاقة منطق الاختبار؛ انقلها إلى @CsvFileSource أو إلى مصنع @MethodSource بدلاً من ذلك.
الأخطاء الشائعة في JUnit الاختبارات ذات المعلمات
معظم حالات الفشل المتعلقة بالمعاملات هي أخطاء تهيئة تحدث قبل تنفيذ أي تأكيد. يوضح الجدول أدناه الرسائل الأكثر شيوعًا وأسباب ظهورها.
| رسالتك | سبب | حل |
|---|---|---|
| يجب أن يحتوي صنف الاختبار على مُنشئ عام واحد فقط | لا يُعلن الصنف عن أي مُنشئ عام، أو اثنين منهما. | احتفظ بدالة إنشاء عامة واحدة تتطابق معلماتها مع أعمدة البيانات |
| لا توجد طريقة معلمات ثابتة عامة في الفئة | الدالة @Parameters ليست عامة ثابتة، أو تُرجع نوعًا خاطئًا | قم بتعريفه كمجموعة عامة ثابتة وقم بإرجاع Arrays.asList(…) |
| استثناء IllegalArgumentException: عدد الوسائط غير صحيح | يكون الصف أعرض أو أضيق من قائمة معلمات المُنشئ | اجعل عرض كل مصفوفة في المجموعة مساويًا لعرض المنشئ. |
| خطأ في التكوين: لا يوجد موفر وسائط | يحمل اختبار جوبيتر @ParameterizedTest بدون تعليق توضيحي للمصدر | أضف @ValueSource أو @CsvSource أو @CsvFileSource أو @MethodSource أو @EnumSource |
| يبدو أن شرح المصدر لا يفعل شيئًا. | تمت إضافة التعليق التوضيحي @Test إلى الطريقة بدلاً من @ParameterizedTest | استبدل @Test بـ @ParameterizedTest واستورد junit-jupiter-params |
ومن بين الفخاخ الأخرى حالة البيانات المشتركة. لأن JUnit يُنشئ نسخة جديدة لكل صف، وتظل جميع البيانات المخزنة في الحقول الثابتة محفوظة في كل تكرار، ويمكن للقيمة المكتوبة بواسطة الصف [0] أن تُغير نتيجة الصف [4] دون أن تُلاحظ. احتفظ بحالة كل صف في حقول النسخة، وأعد ضبط الموارد المشتركة في الدالة @Before أو @BeforeEach. تُغطى الإرشادات العامة حول عزل الاختبارات في... وحدة التجارب البرنامج التعليمي.









