CodeGym /בלוג Java /Random-HE /בדיקת יחידות ב-Java עם JUnit
John Squirrels
רָמָה
San Francisco

בדיקת יחידות ב-Java עם JUnit

פורסם בקבוצה

מהי בדיקת יחידות ב-Java?

לפני שנתחיל ללמוד JUnit ב-Java, בואו נסקור בקצרה מהי בדיקת יחידות ומדוע היא כל כך פופולרית (אם אתה מכיר את הדברים האלה כבר, דלג ל'כיצד אני כותב מבחן JUnit ב-Java?'). בדיקת יחידות ב-Java הופכת פיתוח תוכנה בקנה מידה גדול להרבה יותר יעיל וללא מאמץ. זה יכול לעזור גם ליחידים וגם לצוותים לקצץ אינספור שעות של ניפוי באגים ולייעל את תהליך שיתוף הפעולה מאוד. בדיקת יחידות ב-Java עם JUnit - 1

https://junit.org/junit4/

הרעיון המהותי של בדיקת יחידות הוא זה: כתוב בדיקות אטומיות של תכונות בודדות (הנקראות בדיקות יחידה) ולאט לאט הוסף תכונות נוספות לאחר בדיקה והבטחת שהקודמים עובדים. זה רעיון פשוט מאוד אך רב עוצמה. כדוגמה לאופן שבו התהליך הזה עשוי להיראות, דמיינו שאתם בונים מחשבון מדעי וירטואלי. נוסף על האופרטורים האריתמטיים לכאורה ( +, -, x, %), למחשבון זה יהיו תכונות מתקדמות הדורשות תכונות משנה אחרות לעבוד בתוכו. כדי לחשב מעריכים, המחשבון שלך צריך להיות מסוגל להכפיל נכון. אז גישת בדיקת יחידות לבנייה ובדיקה של מחשבון זה תהיה:
  • כתוב פונקציית חיבור. בדוק את זה בזהירות, שנה את זה, חזור עד שזה עובד.
  • עשה את אותו הדבר עבור פונקציות חיסור, כפל, חילוק.
  • השתמש באופרטורים הבסיסיים האלה כדי לכתוב פונקציות אופרטור מתקדמות יותר כמו אקספוננטים, ואז בדוק גם את הפונקציות האלה.
זה מבטיח שתכונות שבונות תכונות משנה קטנות יותר לא רק פועלות כהלכה בפני עצמן, אלא שאין בהן תכונות משנה פגומות. לדוגמה, אם אני בודק את פונקציית המעריך ומשהו משתבש, אני יודע שהבאג כנראה אינו בתכונת המשנה של הכפל, מכיוון שפונקציית הכפל כבר נבדקה בהרחבה. זה מבטל באופן ניכר את הכמות הכוללת של הקוד שאני צריך לעקוב אחורה ולבדוק כדי למצוא את הבאג. יש לקוות, דוגמה טריוויאלית זו מבהירה כיצד תהליך החשיבה סביב בדיקת יחידות בנוי. אבל איך בדיקות יחידות מקיימות אינטראקציה עם שאר תהליך פיתוח התוכנה? מה אם יש לך תכונות מורכבות עוד יותר, שצריכות להיות מסוגלות לעבוד ולתקשר יחד? בדיקות יחידות אינן מספיקות כדי להבטיח שתכונות מורכבות כאלה יכולות לעבוד כהלכה ביחד. למעשה, זה רק השלב הראשון של ארבע הרמות של בדיקת תוכנה (אני משתמש באותיות גדולות כי אני מתייחס לתקן התעשייה או לגישה הנפוצה ביותר לבדיקת תוכנה). שלושת השלבים האחרונים הם בדיקת אינטגרציה , בדיקת מערכת ובדיקת קבלה . כל אלה כנראה מתכוונים בדיוק למה שאתה חושב שהם עושים, אבל הרשו לי להבהיר: בדיקת אינטגרציה היא מה שהיינו עושים כדי להבטיח לאלו כפי שהוזכרו לעיל, "תכונות מורכבות", אינטראקציה נכונה זו עם זו. (למשל, לוודא שהמחשבון יכול להתמודד עם "3 + 7 * 4 - 2") בדיקת מערכת היא בדיקת העיצוב הכולל של מערכת מסוימת; לעתים קרובות ישנן מספר מערכות של תכונות מורכבות הפועלות יחד במוצר, אז אתה מקבץ אותן למערכות ובודק אותן בנפרד. (לדוגמה, אם היית בונה מחשבון גרפי, תחילה היית בונה את 'המערכת' האריתמטית כדי להתמודד עם מספרים, בודקת עד שהיא פועלת כמתוכנן, ואז היית בונה ובודקת את 'המערכת' הגרפית כדי להתמודד עם משיכה, כמו זה יבנה מהמערכת האריתמטית). בדיקת קבלה היא בדיקה ברמת המשתמש; זה לראות אם כל המערכות יכולות לעבוד בסנכרון כדי ליצור מוצר מוגמר מוכן להתקבל על ידי משתמשים (למשל, משתמשים הבודקים את המחשבון). מפתחי תוכנה יכולים לפעמים להתעלם מהשלב האחרון של התהליך, מכיוון שלעתים קרובות חברות יגרמו לעובדים אחרים לפרוס מבחני משתמש (בטא) בנפרד.

איך אני כותב מבחן JUnit ב-Java?

כעת, לאחר שיש לך מושג ברור יותר לגבי היתרונות והמגבלות של בדיקת יחידות, בוא נסתכל על קוד כלשהו! אנו נשתמש במסגרת פופולרית לבדיקות 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 בה אנו משתמשים כדי לבדוק את הפונקציות שלנו היא to 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), ומכנים אותו 'צפוי' לפי מוסכמה. ערכו הוא התשובה לה אנו מצפים (4). שורה 8: אנו מכריזים על finalמשתנה ' ' מסוג החזרה של Math.add (int), ומכנים אותו 'ממשי' לפי מוסכמה. הערך שלו הוא תוצאה של Math.add(2, 2). שורה 9: קו הזהב. זו השורה שמשווה בין בפועל לצפוי ואומרת לנו שעברנו את המבחן רק אם הם שווים. הפרמטר הראשון שעבר "2+2 הוא 4" הוא תיאור של פונקציית הבדיקה.

מה אם הפונקציה שלי צריכה לזרוק חריג?

אם דוגמת הבדיקה הספציפית שלך צריכה לזרוק חריג במקום לקבוע שערך בפועל והערך הצפוי שווים, אז ל-JUnit יש דרך להבהיר זאת בכותרת @Test. בואו נסתכל על דוגמה למטה. בהנחה שיש לנו פונקציה ב- Math.javacall 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. אם יש לך משהו להוסיף - אל תהסס לעשות זאת בתגובות למטה. ש: איזו שיטה אתה יכול לקרוא בשיטת הבדיקה שלך כדי להיכשל אוטומטית במבחן? ת: fail("תיאור שגיאה כאן!"); ש: אתה בודק כיתת כלבים; כדי לבדוק אובייקט של כלב, עליך להפעיל אותו לפני שתוכל להריץ עליו בדיקות. אז אתה כותב פונקציה setUp() כדי להפעיל את הכלב. אתה רוצה להפעיל את הפונקציה הזו רק פעם אחת במהלך כל הבדיקות. מה עליך לשים ישירות מעל חתימת הפונקציה setUp() כדי ש-JUnit תדע להפעיל את setUp() לפני הפעלת הבדיקות? ת: @BeforeClass (@BeforeAll ב-JUnit 5) ש: מה חייבת להיות חתימת הפונקציה של הפונקציה setUp() שתוארה למעלה? ת: ריק סטטי ציבורי. כל פונקציה עם @BeforeClass (@BeforeAll ב-JUnit 5) או @AfterClass (@AfterAll ב-JUnit 5) צריכה להיות סטטית. ש: סיימת לבדוק את שיעור הכלב. אתה כותב פונקציה void tearDown() אשר מנקה נתונים ומדפיסה מידע למסוף לאחר כל בדיקה. אתה רוצה שפונקציה זו תפעל לאחר כל בדיקה בודדת. מה עליך לשים ישירות מעל חתימת הפונקציה tearDown() כדי ש-JUnit תדע להפעיל את tearDown() לאחר הפעלת כל בדיקה? ת: @After (@AfterEach ביחידה 5)
הערות
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION