JUnit اختبار الاستثناء المتوقع: @Test(متوقع)

⚡ ملخص ذكي

JUnit يؤكد اختبار الاستثناءات أن الدالة تُطلق الخطأ الذي من المفترض أن تُطلقه، باستخدام المعامل المتوقع الاختياري للتعليق التوضيحي @Test في JUnit 4 وطريقة assertThrows في JUnit 5.

  • 🔘 الغرض: أثبت أن الإدخال غير الصالح ينتج الاستثناء الموثق بدلاً من إجابة خاطئة صامتة.
  • ☑️ بناء الجملة: @Test(expected = ArithmeticException.class) ينجح فقط عندما يتم طرح نوع الاستثناء المحدد هذا.
  • ✅ على سبيل المثال: JUnitتقوم الرسالة بالقسمة على صفر، ويتوقع اختبار AirthematicTest الاستثناء الحسابي الناتج.
  • 🧪 البديل: تتيح لك كتلة try متبوعة بـ fail() فحص الاستثناء الذي تم التقاطه قبل التأكيد عليه.
  • 🛠️ JUnit 5: تقوم الدالة assertThrows بإرجاع الاستثناء الذي تم التقاطه، وبالتالي يمكن تأكيد الرسالة والسبب أيضًا.
  • 📌 مأزق: يتم تمرير المعلمة المتوقعة أينما يحدث الاستثناء، مما قد يخفي فشلاً في كود الإعداد.

JUnit اختبار الاستثناء المتوقع باستخدام المعامل @Test(expected)

JUnit يوفر المرفق لـ trace الاستثناء وأيضًا للتحقق مما إذا كان الكود يطرح الاستثناء المتوقع أم لا.

JUnit يوفر الإصدار 4 طريقة سهلة وواضحة لاختبار الاستثناءات. يمكنك استخدام:

  • المعامل الاختياري (المتوقع) لـ @Test annotationو
  • إلى tracيمكن استخدام "fail()" للحصول على المعلومات.

بينما تجريب في حالة الاستثناء، يجب التأكد من أن فئة الاستثناء التي تقدمها في تلك المعلمة الاختيارية هي @Test annotation هو نفس الاستثناء الذي تُطلقه الدالة فعليًا. هذا لأنك تتوقع استثناءً من الدالة التي تستخدمها وحدة التجاربوإلا فإن JUnit سيفشل الاختبار.

مثال: @Test(expected = IllegalArgumentException.class)

باستخدام المعامل "expected"، يمكنك تحديد اسم الاستثناء الذي قد يطرحه اختبارنا. في المثال أعلاه، أنت تستخدم "استثناء الوسيطة غير القانونية"، والذي سيتم طرحه بواسطة الاختبار إذا استخدم المطور وسيطة غير مسموح بها.

مثال باستخدام @Test(expected)

دعونا نفهم اختبار الاستثناءات من خلال إنشاء Java فئة تحتوي على دالة تُطلق استثناءً استثناءستتولى الأمر وتختبره في فصل اختبار. ضع في اعتبارك JUnitيحتوي الملف Message.java على دالة تُجري عملية حسابية. القسمة في السطر 14 تُقسم على صفر، لذا تُطلق الدالة دائمًا استثناءً حسابيًا (ArithmeticException). انظر أدناه:

JUnitفئة الرسائل في Eclipse مع قيام الدالة printMessage بالقسمة على صفر في السطر 14

package guru99.junit;

public class JUnitMessage{

	private String message;

	public JUnitMessage(String message) {
		this.message = message;
	}

public void printMessage(){

	System.out.println(message); 
	int divide=1/0;

}

public String printHiMessage(){ 

	message="Hi!" + message;
	
	System.out.println(message);

	return message;
}

}

Code التفسير:

  • Code خط 7: إنشاء مُنشئ ذو معلمات مع تهيئة الحقل.
  • Code السطر 11-14: ابتكار طريقة لإجراء العملية الحسابية.
  • Code خط 18: إنشاء طريقة أخرى لطباعة الرسالة.
  • Code خط 20: إنشاء سلسلة جديدة لطباعة رسالة.
  • Code خط 22: طباعة الرسالة الجديدة التي تم إنشاؤها في السطر 20.

لنقم بإنشاء فئة اختبار لما سبق Java فئة للتحقق من الاستثناء.

انظر أدناه فئة الاختبار التي تختبر وحدة الاستثناء (ArithmeticException هنا) الذي تم طرحه من أعلاه Java صف دراسي:

AirthematicTest.java

تُظهر لقطة الشاشة أدناه نفس الاختبار في المحرر، حيث تم حفظ الملف باسم AirthematicTest1، مع تمييز المعلمة المتوقعة في السطر 13:

فئة AirthematicTest1 في Eclipse مع تمييز @Test(expected = ArithmeticException.class) في السطر 13

package guru99.junit;

import static org.junit.Assert.assertEquals;

import org.junit.Test;

public class AirthematicTest {

	public String message = "Saurabh";
	
	JUnitMessage junitMessage = new JUnitMessage(message);
	
	@Test(expected = ArithmeticException.class)
	public void testJUnitMessage(){

		System.out.println("Junit Message is printing ");
		junitMessage.printMessage();

	}

	@Test
	public void testJUnitHiMessage(){ 
		message="Hi!" + message;
		System.out.println("Junit Message is printing ");
		assertEquals(message, junitMessage.printHiMessage());
	
	}
}

Code التفسير:

  • Code خط 13: نستخدم التعليق التوضيحي @Test لإنشاء اختبارنا. عند تنفيذ دالة الفئة المذكورة أعلاه، سيتم استدعاء عملية حسابية. هنا، يُتوقع حدوث استثناء ArithmeticException، لذا يتم إدراجه كمعامل في @Test.
  • Code خط 17: استدعاء دالة printMessage() من JUnitرسالة.جافا.
  • Code السطر 21-22: إنشاء طريقة اختبار أخرى للتحقق من رسالة "مرحباً"، هذه المرة بدون معلمة متوقعة.

تحتوي الفئة على طريقتين للاختبار، لذا فإن تشغيلًا واحدًا ينفذ كلتيهما: الطريقة التي تتوقع ArithmeticException والطريقة التي تؤكد على السلسلة المُعادة.

ملاحظة: يُشار إلى هذا المثال بثلاثة أسماء في المادة الأصلية؛ حيث يُطلق على الفئة في القائمة اسم AirthematicTest، بينما تُظهر لقطة شاشة المحرر اسم AirthematicTest1، وتُظهر نافذة النتائج اسم JunitTestExample. الكود متطابق في كل حالة، والاختلاف الوحيد هو اسم الملف.

لنقم بتنفيذه ونتحقق من النتيجة. JUnit يعرض التقرير أدناه سير العمل مثال على اختبار Junit.java.

الإخراج:

هذه هي النتيجة، والتي تُظهر اختبارًا ناجحًا بدون أي فشل. trace كما هو موضح أدناه:

Eclipse JUnit عرض تقرير التشغيل: تم تشغيل 2/2 بدون أخطاء أو حالات فشل لـ JunitTestExample

كلا الطريقتين ناجحتان. الأولى ناجحة لأن الاستثناء الحسابي الذي تم الإعلان عنه قد وصل، والثانية ناجحة لأن السلسلة المُعادة كانت مطابقة. لو لم يحدث أي خطأ في القسمة، JUnit كان من الممكن أن تفشل الطريقة الأولى مع ظهور الرسالة "استثناء متوقع: java.lang.ArithmeticException".

ثلاث طرق لاختبار استثناء في JUnit 4

المعلمة المتوقعة هي الأقصر من بين الثلاثة JUnit أربعة تعابير، لكن ليس بالضرورة أن يكون التعبير الصحيح هو الأنسب. يقارن الجدول بينها بناءً على السؤالين اللذين يحددان الاختيار: هل يمكنك تأكيد الرسالة، وهل تعرف أي سطر تم استخدامه بشكل خاطئ؟

النهج هل تؤكد الرسالة؟ يثبت خط الرمي؟ أفضل ل
@Test(expected = X.class) لا لا — قد يُطلق أي سطر في الدالة خطأً. اختبارات قصيرة لا يهم فيها سوى نوع الاستثناء.
حاول / فشل() / أمسك نعم، داخل كتلة الإمساك نعم، تتم مراقبة المكالمات المحمية فقط. الاختبارات التي يجب أن تفحص الرسالة أو السبب.
@Rule ExpectedException نعم، من خلال expectMessage() لا مجموعات البرامج القديمة مبنية بالفعل على قواعد.

يبدو أسلوب fail() المذكور في المقدمة كالتالي. إذا لم يُصدر الاستدعاء أي خطأ، فسيتم تشغيل fail() وسيُبلغ الاختبار عن الرسالة التي كتبتها:

@Test
public void testDivideByZero() {
    try {
        junitMessage.printMessage();
        fail("Expected an ArithmeticException");
    } catch (ArithmeticException e) {
        assertEquals("/ by zero", e.getMessage());
    }
}

تقع قاعدة الاستثناء المتوقع بين الاثنين. وقد تم إيقاف استخدامها في JUnit 4.13 لصالح طريقة assertThrows الموضحة لاحقًا، لذا جديد JUnit لا ينبغي اعتماد الاختبارات الأربعة.

كيفية اختبار الاستثناءات في JUnit 5 مع assertThrows()

JUnit 5 يزيل المعامل المتوقع من @Test تمامًا. يوفر Jupiter assertThrows()تأخذ هذه الدالة فئة الاستثناء ودالة لامدا تحتوي على الكود قيد الاختبار. وتعيد الاستثناء الذي التقطته، بحيث يمكن التحقق من الرسالة والسبب وأي حقل مخصص لاحقًا.

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.junit.jupiter.api.Test;

public class AirthematicJupiterTest {

    JUnitMessage junitMessage = new JUnitMessage("Saurabh");

    @Test
    public void testJUnitMessage() {
        ArithmeticException thrown = assertThrows(
                ArithmeticException.class,
                () -> junitMessage.printMessage());
        assertEquals("/ by zero", thrown.getMessage());
    }
}

ثلاثة مترابطة تأكيدات استكمال العائلة:

  • assertThrows يقبل نوع الاستثناء أو أي فئة فرعية منه.
  • assertThrowsExactly يرفض النوع الفرعي، لذلك يمر النوع المسمى فقط.
  • assertDoesNotThrow وينص على التوقع المعاكس، وهو أن الكتلة تكتمل بسلاسة.

استخدم JUnit لا يزال الكود رقم 4 الموجود في هذه الصفحة يعمل على JUnit يتم استخدام المنصة من خلال المحرك القديم، لذلك لا يلزم إعادة كتابة أي شيء مما سبق لضمان استمرار العمل أثناء ترحيل المشروع.

الأخطاء الشائعة عند اختبار الاستثناءات في JUnit

تفشل اختبارات الاستثناءات في عدد قليل من الطرق التي يمكن التعرف عليها. يحدد كل سطر العرض والسبب والحل.

عرض سبب حل
الاستثناء المتوقع: java.lang.ArithmeticException اكتملت العملية بنجاح دون حدوث أي خطأ. تأكد من أن المدخلات غير صالحة بالفعل، ثم أعد تشغيل البرنامج.
اجتاز الاختبار، لكن السطر الخاطئ تم تمريره. يراقب المعامل المتوقع الطريقة بأكملها، بما في ذلك الإعداد. انقل الإعداد إلى الخارج، أو قم بالتبديل إلى assertThrows حول استدعاء واحد.
نوع استثناء غير معالج في المحرر يتم طرح استثناء تم التحقق منه ولكن لا يتم الإعلان عنه مطلقًا. أضف عمليات الرمي إلى توقيع طريقة الاختبار.
اجتاز الاختبار على فئة فرعية لم تكن تنويها تقبل الدالة assertThrows الفئات الفرعية من النوع المسمى. استخدم assertThrowsExactly للمطابقة الدقيقة للأنواع.
السمة المتوقعة ليست سمة صالحة تم تجميع الاختبار باستخدام تعليق Jupiter @Test. استورد org.junit.Test لـ JUnit 4، أو انتقل إلى assertThrows.

السطر الأخير هو الذي يُربك معظم الناس أثناء عملية الترحيل، لأن كلا التعليقين يحملان اسم @Test، والفرق بينهما يكمن فقط في عملية الاستيراد. Keeping صورة واحدة؟ JUnit إصدار لكل حالة اختبار يتجنب هذا الصنف فئة المشاكل بأكملها.

الأسئلة الشائعة

لا يمكن للمعامل المتوقع التحقق من رسالة. قم بتغليف الاستدعاء بكتلة try، ثم استدعِ fail() مباشرةً بعده، وتحقق من getMessage() داخل كتلة catch. JUnit 5 يجعل هذا الأمر أبسط لأن assertThrows يعيد الاستثناء الذي تم التقاطه.

JUnit يفشل الاختبار ويُبلغ عن "استثناء متوقع" متبوعًا باسم الفئة التي حددتها. لا يتم الإبلاغ عن أي شيء آخر، لذا فإن الاختبار الذي لا يُصدر أي استثناء يبدو مطابقًا لاختبار آخر تم تغيير سلوكه في كود الإنتاج دون علم المستخدم.

تقوم المساعدات الذكية بقراءة دالة، وسرد المدخلات التي تصل إلى كل عبارة throw، وصياغة اختبار لكل فرع. يتم التعامل مع المخرجات كنقطة انطلاق: يستنتج المساعد نوع الاستثناء من الكود، لذا يتم نسخ عبارة throw الخاطئة بدقة إلى الاختبار.

مساعد طيار يتبع النمط الموجود مسبقًا في الملف، لذا فإن المشروع المختلط يحصل على مزيج. تحقق من سطر الاستيراد قبل قبول أي اقتراح، لأن org.junit.Test و org.junit.jupiter.api.Test تبدوان متطابقتين في المحرر.

لا يزال موجودًا، ولكن تم إيقاف استخدامه في JUnit 4.13 ولم يتم نقله إلى جوبيتر. يمكن للمجموعات الحالية الاحتفاظ به؛ يجب أن تستخدم الاختبارات الجديدة assertThrows، وهو أفضل قراءةً ولا يحتاج إلى حقل قاعدة عام.

نعم. مُعامل لامدا هو كائن تنفيذي، مُصرّح له برمي استثناء من نوع Throwable، لذا لا يحتاج الاستثناء المُدقّق إلى عبارة throws في دالة الاختبار نفسها. وينطبق الأمر نفسه على... JUnit 4. حاول والتقط (تعبير مجازي).

تقبل الدالة assertThrows أي فئة فرعية من النوع الذي تحدده، لذا فإن توقع RuntimeException يؤدي أيضًا إلى تمرير NullPointerException. استخدم assertThrowsExactly عندما يكون تحديد الفئة بدقة أمرًا بالغ الأهمية، أو حدد أضيق نوع تتوقعه فعليًا.

فقط عندما يكون الاستثناء جزءًا من الخدعةtract. اختبار الاستثناء لا يعد الأسلوب أبدًا برمي الأقفال في السلوك العرضي ويجعل إعادة الهيكلة أكثر صعوبة، مما يهزم الغرض من الاختبار.

تلخيص هذه التدوينة بـ: