JUnit هو إطار عمل قياسي للاختبارات الوحدوية في بيئة Java وKotlin، يُستخدم في تطوير Android للتحقق من منطق الأعمال على مستوى المكونات المعزولة. يوفر الإطار مجموعة من التعليقات التوضيحية وطرق التأكيد وTest Runner للاكتشاف التلقائي للاختبارات وتنفيذها. وفقًا لـ JUnit.org، تظل المكتبة الحل الأكثر شيوعًا للاختبارات الوحدوية في بيئة JVM: أكثر من 70% من مشاريع Java تستخدم JUnit بأي إصدار.
الرئيسية
JUnit هو إطار عمل مفتوح المصدر لكتابة وتشغيل اختبارات وحدوية قابلة للتكرار بلغة Java وKotlin. إنه جزء من بيئة xUnit — عائلة من الأطر المبنية على بنية Kent Beck وErich Gamma — وهو أداة الاختبار القياسية في Android Studio وIntelliJ IDEA.
المهمة الرئيسية لـ JUnit هي عزل جزء صغير من الكود (طريقة، فئة) والتحقق من سلوكه في ظروف محكومة. تُكتب الاختبارات كفئات Java/Kotlin عادية مع تعليقات توضيحية، ويتولى الإطار اكتشاف الاختبارات وإدارة دورة حياتها وجمع إحصائيات التنفيذ.
ظهر الإصدار الأول من JUnit في 1997 وغيّر نهج التطوير بشكل جذري من خلال نشر ممارسات التطوير القائم على الاختبار (TDD). اليوم، JUnit 5 (Jupiter) هو الإصدار الحالي، المعاد تصميمه بالكامل ببنية معيارية تدعم Java 8+ والإضافات.
آلية تنفيذ JUnit تستند إلى نمط Test Runner، الذي يمسح classpath، ويجد الطرق ذات التعليق التوضيحي @Test، وينشئ مثيلاً لفئة الاختبار ويشغّل الطرق بترتيب معين. يدير Test Runner دورة الحياة: BeforeAll ← BeforeEach ← Test ← AfterEach ← AfterAll.
يغلف JUnit كل طريقة اختبار في مثيل فئة منفصل، مما يضمن العزل بين الاختبارات. هذا يعني أن حقول الفئة لا تحتفظ بالحالة بين طرق @Test المختلفة — يبدأ كل اختبار بكائن نظيف.
أبسط اختبار في JUnit هو طريقة مع التعليق التوضيحي @Test تستدعي الكود قيد الاختبار وتتحقق من النتيجة باستخدام تأكيد. إذا فشل التأكيد، يعتبر الاختبار فاشلاً ويبلغ JUnit عن الخطأ.
@Test
void additionShouldReturnCorrectSum() {
var calculator = new Calculator();
var result = calculator.add(2, 3);
assertEquals(5, result);
}
يجد Test Runner تلقائيًا مثل هذه الطريقة، ويشغلها ويبلغ عن النتيجة. إذا لم ترمِ الطريقة استثناءً — ينجح الاختبار (green). إذا فشل التأكيد — يفشل الاختبار (red).
التعليقات التوضيحية في JUnit تحدد متى وكيف يجب تنفيذ كود الاختبار. في JUnit 5، توجد التعليقات التوضيحية في الحزمة org.junit.jupiter.api وتغطي جميع المراحل: إعداد البيانات، تنفيذ الاختبار، تنظيف الموارد.
| التعليق التوضيحي | الغرض | التنفيذ |
|---|---|---|
| @Test | يحدد طريقة اختبار | مرة واحدة لكل استدعاء |
| @BeforeEach | الإعداد قبل كل اختبار | قبل كل @Test |
| @BeforeAll | تهيئة الفئة لمرة واحدة | مرة واحدة قبل جميع الاختبارات |
| @AfterEach | التنظيف بعد كل اختبار | بعد كل @Test |
| @AfterAll | إنهاء الفئة لمرة واحدة | مرة واحدة بعد جميع الاختبارات |
| @DisplayName | اسم اختبار قابل للقراءة | زخرفة |
لنفكر في فئة اختبار كاملة مع دورة حياة مناسبة. تنشئ طريقة setUp مثيلاً جديدًا من Calculator قبل كل اختبار، وتحرر tearDown الموارد — على سبيل المثال، إغلاق واصفات الملفات أو اتصالات قاعدة البيانات.
class CalculatorTest {
private Calculator calculator;
@BeforeEach
void setUp() {
calculator = new Calculator();
}
@Test
void subtractionShouldReturnCorrectResult() {
int result = calculator.subtract(10, 4);
assertEquals(6, result);
}
@AfterEach
void tearDown() {
calculator.reset();
}
}
التأكيدات هي طرق ثابتة تقارن النتيجة الفعلية بالنتيجة المتوقعة وتطرح استثناءً في حالة عدم التطابق. يوفر JUnit 5 الفئة org.junit.jupiter.api.Assertions مع أكثر من 25 طريقة لسيناريوهات التحقق المختلفة — من مقارنة القيم البسيطة إلى فحوصات المهلة والتأكيدات المجمعة.
يستحق الأسلوب assertThrows اهتمامًا خاصًا، حيث يسمح بالتحقق من أن الكود يعالج بشكل صحيح حالات الخطأ — القسمة على صفر، تمرير null، تجاوز الحدود.
@Test
void divisionByZeroShouldThrowException() {
Calculator calc = new Calculator();
ArithmeticException exception = assertThrows(
ArithmeticException.class,
() -> calc.divide(10, 0)
);
assertEquals("Cannot divide by zero", exception.getMessage());
}
الاختبارات المعلمة تسمح بتشغيل نفس طريقة الاختبار بمجموعات مختلفة من الوسائط. في JUnit 5، يتم ذلك باستخدام التعليق التوضيحي @ParameterizedTest مع مصدر بيانات — @ValueSource أو @CsvSource أو @MethodSource أو @EnumSource.
يقلل هذا النهج بشكل جذري من تكرار الكود: بدلاً من عشرة اختبارات متطابقة لقيم مختلفة، يُكتب طريقة واحدة معلمة. وفقًا لـ Google Testing Blog، يقلل التوسيم من حجم كود الاختبار بنسبة 40–60% دون فقدان التغطية.
يمرر التعليق التوضيحي @CsvSource صفوفًا متعددة مفصولة بفواصل إلى الاختبار. كل صف يتوافق مع تشغيل اختبار واحد. يقوم JUnit تلقائيًا بتحويل القيم النصية إلى الأنواع المطلوبة: int وlong وString وغيرها.
@ParameterizedTest
@CsvSource({
"1, 1, 2",
"2, 3, 5",
"10, 20, 30",
"-1, 1, 0"
})
void additionWithMultipleInputs(int a, int b, int expected) {
assertEquals(expected, a + b);
}
عندما تكون بيانات الإدخال أكثر تعقيدًا من الأرقام أو السلاسل البسيطة، يُستخدم @MethodSource. يشير إلى طريقة ثابتة تعيد Stream من الوسائط — على سبيل المثال، Stream من Arguments أو Stream من كائنات User لاختبار طبقة DAO.
في تطوير Android، يُستخدم JUnit جنبًا إلى جنب مع AndroidX Test، الذي يوفر إضافات لاختبار Activity وContent Provider ومكونات Android الأخرى. يتولى JUnit الاختبارات الوحدوية التي تُنفذ على JVM بدون محاكي، بينما يتولى AndroidX Test الاختبارات الآلية على الجهاز.
لإضافة JUnit 5 إلى مشروع Android، يكفي تضمين التبعية org.junit.jupiter:junit-jupiter في build.gradle على مستوى الوحدة. يدعم إضافة AGP لـ Gradle تشغيل اختبارات JUnit 5 على JVM من خلال Test Runner القياسي.
// build.gradle.kts
android {
testOptions {
unitTests.isIncludeAndroidResources = true
}
}
dependencies {
testImplementation("org.junit.jupiter:junit-jupiter:5.11.0")
testImplementation("androidx.test:core-ktx:1.6.1")
}
في الممارسة العملية، يُستخدم JUnit في أغلب الأحيان لاختبار ViewModel والمستودعات — الطبقات التي لا تتطلب سياق Android أو محاكي. يتم تنفيذ هذا الاختبار في أجزاء من الثانية ويمكن تشغيله مئات المرات دون إضاعة الوقت.
JUnit 5 (Jupiter) ليس مجرد إصدار جديد، بل هو منصة معاد تصميمها بالكامل مقسمة إلى ثلاث وحدات: JUnit Platform (تنفيذ الاختبارات على JVM)، JUnit Jupiter (API لكتابة الاختبارات)، وJUnit Vintage (التوافق مع الإصدارات السابقة لـ JUnit 4). تسمح هذه المعيارية بتوصيل محركات اختبار مختلفة — على سبيل المثال، Spek لـ Kotlin أو TestNG.
| الخاصية | JUnit 4 | JUnit 5 |
|---|---|---|
| الحزمة | org.junit | org.junit.jupiter |
| التعليق التوضيحي الأساسي | @Test (من junit.framework) | @Test (من org.junit.jupiter.api) |
| Before/After | @Before, @After, @BeforeClass | @BeforeEach, @AfterEach, @BeforeAll |
| المعلمة | @RunWith(Parameterized.class) | @ParameterizedTest + @ValueSource |
| الإضافة | @Rule, @ClassRule | @ExtendWith, API أكثر مرونة |
| Java min | Java 5 | Java 8+ |
لا يتطلب الانتقال من JUnit 4 إلى JUnit 5 إعادة كتابة جميع الاختبارات — يكفي إضافة JUnit Vintage Engine وستستمر الاختبارات القديمة في العمل. يُوصى بكتابة الاختبارات الجديدة باستخدام JUnit 5 للاستفادة من الإضافات وتأكيدات lambda والدعم المدمج لـ التوسيم.
الأسئلة المتداولة
JUnit هو إطار عمل لكتابة وتشغيل الاختبارات، بينما Mockito مكتبة لإنشاء كائنات وهمية. يتم استخدامهما معًا: يدير JUnit تنفيذ الاختبار، ويستبدل Mockito تبعيات الفئة قيد الاختبار.
نعم، JUnit 5 متوافق تمامًا مع مشاريع Android. للاختبارات الوحدوية، يكفي إضافة التبعية junit-jupiter في build.gradle. تستمر الاختبارات الآلية في العمل من خلال AndroidX Test Runner.
كحد أدنى، يكفي تعليق توضيحي واحد @Test قبل الطريقة. يُوصى باستخدام @BeforeEach و@AfterEach للتهيئة والتنظيف، لكنهما غير إلزاميين.
Test Runner هو مكون في JUnit يمسح classpath، ويجد الطرق ذات التعليق التوضيحي @Test، وينشئ مثيلات فئات الاختبار ويشغل الاختبارات. في JUnit 5، يقوم بهذا الدور JUnit Platform مع محركات الاختبار المتصلة.
استخدم assertThrows(Class, Executable) — يأخذ نوع الاستثناء المتوقع ولامبدا مع استدعاء الطريقة. يتحقق JUnit من أن الاستثناء قد أُلقِي فعلاً ويعيده لمزيد من الفحص.
الخلاصة
سنقوم بتطوير تطبيق جوال جاهز
تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.
اقرأ أيضًا