CodeGym /مدونة جافا /Random-AR /اختبار الوحدة في Java باستخدام JUnit
John Squirrels
مستوى
San Francisco

اختبار الوحدة في Java باستخدام JUnit

نشرت في المجموعة

ما هو اختبار الوحدة في جافا؟

قبل أن نبدأ في تعلم JUnit في Java، دعنا نلقي نظرة عامة مختصرة على اختبار الوحدة ولماذا يحظى بشعبية كبيرة (إذا كنت تعرف هذه الأشياء بالفعل، فانتقل إلى "كيف أكتب اختبار JUnit في Java؟"). اختبار الوحدة في Java يجعل تطوير البرامج على نطاق واسع أكثر كفاءة وأقل جهدًا. يمكن أن يساعد الأفراد والفرق على حد سواء في قطع ساعات لا حصر لها من تصحيح الأخطاء وتبسيط عملية التعاون بشكل كبير. اختبار الوحدة في Java باستخدام JUnit - 1

https://junit.org/junit4/

الفكرة الأساسية لاختبار الوحدة هي: كتابة اختبارات ذرية للميزات الفردية (تسمى اختبارات الوحدة) وإضافة المزيد من الميزات ببطء بعد الاختبار والتأكد من عمل الميزات السابقة. إنها فكرة بسيطة للغاية ولكنها قوية. كمثال لكيفية ظهور هذه العملية، تخيل أنك تقوم ببناء آلة حاسبة علمية افتراضية. علاوة على العوامل الحسابية الظاهرة ( +،،،،، ) ، ستحتوي هذه الآلة الحاسبة على ميزات متقدمة تتطلب ميزات فرعية أخرى للعمل بداخلها -. لحساب الأسس، يجب أن تكون الآلة الحاسبة الخاصة بك قادرة على الضرب بشكل صحيح. لذا فإن طريقة اختبار الوحدة لبناء واختبار هذه الآلة الحاسبة ستكون كما يلي: x%
  • اكتب دالة إضافة. اختبره بعناية، وقم بتغييره، وكرر ذلك حتى يعمل.
  • افعل الشيء نفسه بالنسبة لوظائف الطرح والضرب والقسمة.
  • استخدم عوامل التشغيل الأساسية هذه لكتابة دوال معاملات أكثر تقدمًا مثل الأسس، ثم اختبر تلك الوظائف أيضًا.
وهذا يضمن أن الميزات التي تبني ميزات فرعية أصغر أخرى لا تعمل بشكل صحيح في حد ذاتها فحسب، بل لا تحتوي على ميزات فرعية معيبة بداخلها. على سبيل المثال، إذا كنت أقوم باختبار الدالة الأسية وحدث خطأ ما، فأنا أعلم أن الخطأ ربما لا يكون موجودًا في ميزة الضرب الفرعية، لأن وظيفة الضرب تم اختبارها على نطاق واسع بالفعل. يؤدي هذا إلى التخلص بشكل كبير من إجمالي كمية التعليمات البرمجية التي أحتاج إلى تتبعها وفحصها للعثور على الخطأ. نأمل أن يوضح هذا المثال التافه كيفية تنظيم عملية التفكير حول اختبار الوحدة. ولكن كيف يتفاعل اختبار الوحدة مع بقية عملية تطوير البرمجيات؟ ماذا لو كان لديك ميزات أكثر تعقيدًا، والتي تحتاج إلى أن تكون قادرًا على العمل والتواصل معًا؟ اختبار الوحدة غير كافٍ للتأكد من أن هذه الميزات المعقدة يمكن أن تعمل معًا بشكل صحيح. في الواقع، إنها مجرد خطوة أولى من المستويات الأربعة لاختبار البرمجيات (أستخدم الحروف الكبيرة لأنني أشير إلى معيار الصناعة أو النهج الأكثر شيوعًا لاختبار البرامج). الخطوات الثلاث الأخيرة هي اختبار التكامل ، واختبار النظام ، واختبار القبول . ربما تعني كل هذه الأمور بالضبط ما تعتقد أنهم يفعلونه، ولكن دعني أوضح: اختبار التكامل هو ما سنفعله لضمان تفاعل "الميزات المعقدة" كما هو مذكور أعلاه بشكل صحيح مع بعضها البعض. (على سبيل المثال، التأكد من قدرة الآلة الحاسبة على التعامل مع "3 + 7 * 4 - 2") اختبار النظام هو اختبار التصميم العام لنظام معين؛ غالبًا ما تكون هناك أنظمة متعددة ذات ميزات معقدة تعمل معًا في المنتج، لذا يمكنك تجميعها في أنظمة واختبارها بشكل فردي. (على سبيل المثال، إذا كنت تقوم ببناء آلة حاسبة رسومية، عليك أولاً إنشاء "النظام" الحسابي للتعامل مع الأرقام، واختباره حتى يعمل على النحو المنشود، ثم إنشاء واختبار "نظام" الرسوم البيانية للتعامل مع السحب، كما سوف ينطلق من النظام الحسابي). اختبار القبول هو اختبار على مستوى المستخدم؛ إنها ترى ما إذا كانت جميع الأنظمة يمكنها العمل بشكل متزامن لإنشاء منتج نهائي جاهز للقبول من قبل المستخدمين (على سبيل المثال، المستخدمون الذين يختبرون الآلة الحاسبة). يمكن لمطوري البرامج أحيانًا تجاهل هذه الخطوة الأخيرة من العملية، حيث غالبًا ما يكون لدى الشركات موظفون آخرون ينشرون اختبارات المستخدم (الإصدار التجريبي) بشكل منفصل.

كيف أكتب اختبار JUnit في جافا؟

الآن بعد أن أصبحت لديك فكرة أوضح عن فوائد اختبار الوحدة وقيوده، دعنا نلقي نظرة على بعض التعليمات البرمجية! سنستخدم إطار عمل اختبار Java شائعًا يُسمى JUnit (أحد الإطارات الشائعة الأخرى هو TestNG، والذي يمكنك استخدامه أيضًا إذا أردت. إنهما متشابهان جدًا من حيث التركيب؛ TestNG مستوحى من JUnit). يمكنك تنزيل وتثبيت JUnit هنا . بالنسبة لرمز المثال هذا، سنواصل الخروج من مثال "الآلة الحاسبة العلمية" الذي ذكرته سابقًا؛ من السهل جدًا أن تستوعب الأمر، ورمز الاختبار سهل للغاية. الممارسة التقليدية هي كتابة فصول اختبار منفصلة لكل فصل من فصولك، وهذا ما سنفعله. لنفترض أنه في هذه المرحلة، لدينا ملف Math.javaيحتوي على جميع الوظائف الرياضية (بما في ذلك Math.add)، ونحن نكتب ملفًا MathTests.javaفي نفس الحزمة. لنقم الآن بإعداد عبارات الاستيراد ونص الفصل: (سؤال محتمل في مقابلة JUnit: قد يتم سؤالك عن مكان وضع اختبار JUnit الخاص بك وما إذا كنت بحاجة إلى استيراد ملفات المصدر الخاصة بك أم لا. إذا كنت تكتب فئات الاختبار الخاصة بك في نفس الحزمة مثل فئاتك الرئيسية، فلن تحتاج إلى أي بيانات استيراد لملفاتك المصدر في فئة الاختبار. بخلاف ذلك، تأكد من استيراد ملفاتك المصدر!)
import org.junit.jupiter.Test;    //gives us the @Test header
import static org.junit.jupiter.api.Assertions.assertEquals; //less typing :)

public class MathTests {
	//...
}
بيان الاستيراد الأول يعطينا @Testالرأس. نكتب " @Test" مباشرةً أعلى كل تعريف لوظيفة الاختبار، حتى تعرف JUnit أن هذا اختبار وحدة فردية يمكن تشغيله بشكل منفصل. لاحقًا، سأوضح لك كيف يمكنك إجراء اختبارات وحدة معينة باستخدام هذا الرأس. بيان الاستيراد الثاني يوفر لنا القليل من الكتابة. وظيفة JUnit الأساسية التي نستخدمها لاختبار وظائفنا هي Assert.assertEquals()، والتي تأخذ معلمتين (القيمة الفعلية والقيمة المتوقعة) وتتأكد من أنهما متساويان. إن الحصول على بيان الاستيراد الثاني يسمح لنا فقط بكتابة ' assertEquals(...' بدلاً من الاضطرار إلى تحديد الحزمة التي تكون جزءًا منها في كل مرة. الآن دعونا نكتب حالة اختبار بسيطة جدًا للتحقق من أن 2 + 2 يساوي 4 بالفعل!
import org.junit.jupiter.Test; // gives us the @Test header
import static org.junit.jupiter.api.Assertions.assertEquals; // less typing :)


public class MathTests {
	@Test
	public void add_twoPlusTwo_returnsFour(){
	final int expected = 4;
	final int actual = Math.add(2, 2);
	assertEquals(2+2 is 4, actual, expected);
	}
}
دعنا نراجع الأسطر الخمسة لوظيفة الاختبار وما يفعلونه: السطر 5: @Testيحدد هذا الرأس أن تعريف الوظيفة أدناه add_twoPlusTwo_returnsFour()هو بالفعل وظيفة اختبار يمكن لـ JUnit تشغيلها بشكل منفصل. السطر 6: هذا هو التوقيع الوظيفي لحالة الاختبار الخاصة بنا. تكون حالات الاختبار دائمًا فردية جدًا؛ إنهم يختبرون مثالًا محددًا واحدًا فقط، مثل 2+2=4. من المعتاد تسمية حالات الاختبار الخاصة بك بالصيغة " [function]_[params]_returns[expected]()،" حيث [function]يكون اسم الوظيفة التي تختبرها، [params]ومثال المعلمات المحددة التي تختبرها، [expected]وهي قيمة الإرجاع المتوقعة للوظيفة. تحتوي وظائف الاختبار دائمًا تقريبًا على نوع إرجاع ' void' لأن النقطة الرئيسية للوظيفة بأكملها هي التشغيل assertEquals، والتي سيتم إخراجها إلى وحدة التحكم سواء نجح الاختبار أم لا؛ لا تحتاج إلى إرجاع أي بيانات أخرى إلى أي مكان. السطر 7: نعلن عن finalمتغير ' ' من نوع الإرجاع Math.add (int)، ونسميه 'متوقعًا' حسب الاصطلاح. وقيمتها هي الجواب الذي نتوقعه (٤). السطر 8: نعلن عن finalمتغير ' ' من نوع الإرجاع Math.add (int)، ونسميه 'فعليًا' حسب الاصطلاح. قيمتها هي نتيجة Math.add(2, 2). السطر التاسع: الخط الذهبي. هذا هو الخط الذي يقارن بين الفعلي والمتوقع ويخبرنا أننا اجتزنا الاختبار فقط إذا كانا متساويين. المعلمة الأولى التي تم تمريرها "2+2 هي 4" هي وصف لوظيفة الاختبار.

ماذا لو كانت وظيفتي يجب أن تطرح استثناءً؟

إذا كان يجب على مثال الاختبار المحدد الخاص بك أن يطرح استثناءً بدلاً من التأكيد على أن القيمة الفعلية والمتوقعة متساوية، فإن JUnit لديها طريقة لتوضيح ذلك في الرأس @Test. دعونا نلقي نظرة على المثال أدناه. بافتراض أن لدينا دالة Math.javaتسمى Math.divide، نريد التأكد من أن المدخلات لا يمكن تقسيمها على 0. بدلاً من ذلك، فإن محاولة استدعاء Math.divide(a, 0)أي قيمة 'a' يجب أن تؤدي إلى استثناء ( ArithmeticException.class). نحدد ذلك في الرأس على هذا النحو:
import org.junit.jupiter.Test; // gives us the @Test header
import static org.junit.jupiter.api.Assertions.assertEquals; // less typing :)


public class MathTests {
	@Test (expectedExceptions = ArithmeticException.class)
	public void divide_byZero_throwsException() throws ArithmeticException{
	Math.divide(1, 0);
	}
}
يمكن أن يكون لديك أكثر من استثناء واحد لـ expectedExceptions، فقط تأكد من استخدام الأقواس والفواصل لسرد فئات الاستثناءات الخاصة بك، على هذا النحو:
expectedException = {FirstException.class, SecondException.class,}

كيف أقوم بإجراء اختبارات JUnit الخاصة بي في Java؟

كيفية إضافة JUnit إلى IntelliJ: https://stackoverflow.com/questions/19330832/setting-up-junit-with-intellij-idea يمكنك تشغيل مشروعك كما تفعل عادةً مع الاختبارات. سيؤدي تشغيل جميع الاختبارات في فصل اختبار إلى تشغيلها بالترتيب الأبجدي. في JUnit 5، يمكنك إضافة أولوية للاختبارات عن طريق إضافة @Orderعلامة. مثال:
@TestMethodOrder(OrderAnnotation.class)
public class Tests {@Test
@Order(2)
public void a_test() {}

@Test
@Order (1)
public void b_test() {}}
على الرغم من a_test()أنه يأتي قبل b_test()أبجديًا وفي الكود، b_test()فسيتم تشغيله قبل a_test()هنا، لأن 1 يأتي قبل 2 بالترتيب. هذا كل ما يتعلق بأساسيات JUnit. الآن، دعونا نتناول بعض الأسئلة الشائعة في مقابلة JUnit، ونتعلم المزيد عن JUnit على طول الطريق!

أسئلة مقابلة JUnit (معلومات إضافية)

لقد قمت هنا بجمع الأسئلة الأكثر شيوعًا لمقابلة JUnit. إذا كان لديك شيء تريد إضافته – فلا تتردد في القيام بذلك في التعليقات أدناه. س: ما الطريقة التي يمكنك استدعاؤها في طريقة الاختبار الخاصة بك للفشل تلقائيًا في الاختبار؟ ج: فشل("وصف الخطأ هنا!"); س: أنت تقوم باختبار فئة الكلاب؛ لاختبار كائن Dog، تحتاج إلى إنشاء مثيل له قبل أن تتمكن من إجراء الاختبارات عليه. لذلك تكتب دالة setUp() لإنشاء مثيل للكلب. أنت تريد تشغيل هذه الوظيفة مرة واحدة فقط خلال كل الاختبارات. ما الذي يجب عليك وضعه مباشرة فوق توقيع وظيفة setUp() حتى تعرف JUnit كيفية تشغيل setUp() قبل إجراء الاختبارات؟ ج: @BeforeClass (@BeforeAll في JUnit 5) س: ما هو التوقيع الوظيفي للدالة setUp() الموضحة أعلاه؟ ج: الفراغ العام الساكن. أي دالة تحتوي على @BeforeClass (@BeforeAll في JUnit 5) أو @AfterClass (@AfterAll في JUnit 5) يجب أن تكون ثابتة. س: لقد انتهيت من اختبار فئة الكلاب. تكتب وظيفة TeaDown () الفارغة التي تقوم بتنظيف البيانات وطباعة المعلومات إلى وحدة التحكم بعد كل اختبار. تريد تشغيل هذه الوظيفة بعد كل اختبار. ما الذي يجب عليك وضعه مباشرة فوق توقيع وظيفة TeaDown() حتى تعرف JUnit كيفية تشغيل TeaDown() بعد إجراء كل اختبار؟ ج: @After (@AfterEach في الوحدة 5)
تعليقات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION