JavaRush /בלוג Java /Random-HE /בדיקת יחידות ג'אווה: טכניקות, מושגים, תרגול

בדיקת יחידות ג'אווה: טכניקות, מושגים, תרגול

פורסם בקבוצה
היום כמעט ולא תמצאו אפליקציה שאינה מכוסה במבחנים, כך שהנושא הזה יהיה רלוונטי מתמיד למפתחים מתחילים: בלי בדיקות לא תוכלו להגיע לשום מקום. בתור פרסומת, אני מציע שתסתכל במאמרים הקודמים שלי. חלקם מכסים מבחנים (ולמרות זאת המאמרים יהיו שימושיים מאוד):
  1. בדיקת אינטגרציה של מסד נתונים באמצעות MariaDB להחלפת MySql
  2. יישום יישום רב לשוני
  3. שמירת קבצים באפליקציה ונתונים עליהם במסד הנתונים
בואו נבחן באילו סוגי בדיקות משתמשים באופן עקרוני, ולאחר מכן נלמד בפירוט את כל מה שצריך לדעת על בדיקות יחידה.

סוגי בדיקות

מה זה מבחן? כפי שאומר ויקי: " מבחן או מבחן הם דרך לחקור את התהליכים הבסיסיים של מערכת על ידי הצבת המערכת במצבים שונים ומעקב אחר שינויים נצפים בה." במילים אחרות, זהו בדיקה של הפעולה הנכונה של המערכת שלנו במצבים מסוימים. הכל על בדיקת יחידות: שיטות, מושגים, תרגול - 2ובכן, בוא נראה אילו סוגי בדיקות יש:
  1. בדיקת יחידה היא בדיקות שתפקידן לבדוק כל מודול של המערכת בנפרד. רצוי שאלו חלקים של המערכת הניתנים לחלוקה מינימלית, למשל מודולים.

  2. בדיקת מערכת היא בדיקה ברמה גבוהה לבדיקת הפעולה של חלק גדול יותר של אפליקציה או של המערכת כולה.

  3. בדיקת רגרסיה היא בדיקה המשמשת כדי לבדוק האם תכונות חדשות או תיקוני באגים משפיעים על הפונקציונליות הקיימת של האפליקציה והאם באגים ישנים מופיעים שוב.

  4. בדיקה פונקציונלית היא בדיקת תאימות של חלק מהאפליקציה לדרישות המצוינות במפרטים, בסיפורי משתמשים וכו'.

    סוגי בדיקות תפקודיות:

    • בדיקת "קופסה לבנה" לעמידה של חלק מהאפליקציה בדרישות תוך הכרת היישום הפנימי של המערכת;
    • בדיקת "קופסה שחורה" לעמידה של חלק מהאפליקציה בדרישות ללא ידיעה על היישום הפנימי של המערכת.
  5. בדיקת ביצועים היא סוג של בדיקות שנכתבות כדי לקבוע את המהירות שבה מערכת או חלק ממנה פועלים בעומס מסוים.
  6. בדיקת עומסים - בדיקות שנועדו לבדוק את יציבות המערכת בעומסים סטנדרטיים ולמצוא את השיא המקסימלי האפשרי בו האפליקציה פועלת בצורה נכונה.
  7. בדיקת מאמץ היא סוג של בדיקה שנועדה לבדוק את הפונקציונליות של אפליקציה בעומסים לא סטנדרטיים ולקבוע את השיא המקסימלי האפשרי שבו המערכת לא תתרסק.
  8. בדיקות אבטחה - בדיקות המשמשות לבדיקת אבטחת מערכת (מהתקפות של האקרים, וירוסים, גישה לא מורשית לנתונים חסויים ועוד הנאות חיים).
  9. בדיקת לוקליזציה היא בדיקת לוקליזציה עבור אפליקציה.
  10. בדיקת שמישות היא סוג של בדיקה שמטרתה לבדוק שימושיות, מובנות, אטרקטיביות ויכולת למידה עבור המשתמשים.
  11. כל זה נשמע טוב, אבל איך זה עובד בפועל? זה פשוט: נעשה שימוש בפירמידת הבדיקה של מייק קון: הכל על בדיקת יחידות: שיטות, מושגים, תרגול - 4זוהי גרסה פשוטה של ​​הפירמידה: כעת היא מחולקת לחלקים קטנים יותר. אבל היום לא נסוות ונשקול את האפשרות הפשוטה ביותר.
    1. Unit - בדיקות יחידה המשמשות בשכבות שונות של האפליקציה, בודקות את ההיגיון הניתן לחלוקה הקטן ביותר של האפליקציה: למשל, מחלקה, אך לרוב שיטה. בדיקות אלו מנסות בדרך כלל לבודד כמה שניתן מהיגיון חיצוני, כלומר ליצור אשליה ששאר האפליקציה עובדת במצב סטנדרטי.

      תמיד צריכות להיות הרבה בדיקות אלו (יותר מסוגים אחרים), מכיוון שהן בודקות חתיכות קטנות והן מאוד קלות משקל, לא צורכות הרבה משאבים (במשאבים אני מתכוון ל-RAM וזמן).

    2. אינטגרציה - בדיקת אינטגרציה. זה בודק חלקים גדולים יותר של המערכת, כלומר, זה או שילוב של כמה חלקים של לוגיקה (כמה שיטות או מחלקות), או את נכונות העבודה עם רכיב חיצוני. בדרך כלל יש פחות מבדיקות אלו מאשר בדיקות יחידה, מכיוון שהן כבדות יותר.

      כדוגמה למבחני אינטגרציה, אתה יכול לשקול להתחבר למסד נתונים ולבדוק את הביצוע הנכון של השיטות שעובדות איתו .

    3. UI - בדיקות הבודקות את פעולת ממשק המשתמש. הם משפיעים על ההיגיון בכל רמות האפליקציה, ולכן הם נקראים גם מקצה לקצה. ככלל, יש הרבה פחות מהם, מכיוון שהם בעלי המשקל הכבד ביותר וחייבים לבדוק את הנתיבים הנחוצים ביותר (המשומשים).

      באיור לעיל אנו רואים את היחס בין השטחים של חלקים שונים של המשולש: בערך אותו פרופורציה נשמר במספר המבחנים הללו בעבודה אמיתית.

      היום נסתכל מקרוב על המבחנים הנפוצים ביותר - בדיקות יחידות, שכן כל מפתחי Java שמכבדים את עצמם צריכים להיות מסוגלים להשתמש בהם ברמה בסיסית.

    מושגי מפתח של בדיקת יחידות

    כיסוי מבחן (Code Coverage) הוא אחת ההערכות העיקריות של איכות בדיקת היישום. זהו אחוז הקוד שכוסה בבדיקות (0-100%). בפועל, הרבה אנשים רודפים אחרי האחוז הזה, שאני לא מסכים איתו, מאחר שמתחילים להוסיף מבחנים איפה שאין בהם צורך. לדוגמה, לשירות שלנו יש פעולות CRUD (יצירת/קבל/עדכן/מחק) סטנדרטיות ללא היגיון נוסף. שיטות אלו הן מתווכים גרידא המאצילים עבודה לשכבה שעובדת עם המאגר. במצב זה, אין לנו מה לבדוק: אולי אם השיטה הזו קוראת לשיטה מהטאו, אבל זה לא רציני. כדי להעריך את כיסוי הבדיקה, משתמשים בדרך כלל בכלים נוספים: JaCoCo, Cobertura, Clover, Emma וכו'. למחקר מפורט יותר של סוגיה זו, שמור כמה מאמרים מתאימים: TDD (פיתוח מבחן) - פיתוח מונחה מבחן. בגישה זו, קודם כל כותבים מבחן שיבדוק קוד ספציפי. מסתבר שזו בדיקת קופסה שחורה: אנחנו יודעים מה נמצא בקלט ואנחנו יודעים מה צריך לקרות ביציאה. זה ימנע כפילות קוד. פיתוח מונחה בדיקות מתחיל בתכנון ופיתוח בדיקות עבור כל פונקציונליות קטנה של האפליקציה. בגישת TDD, ראשית, פותח מבחן שמגדיר ומוודא מה הקוד יעשה. המטרה העיקרית של TDD היא להפוך את הקוד לבהיר יותר, פשוט יותר וללא שגיאות. הכל על בדיקת יחידות: שיטות, מושגים, תרגול - 6הגישה מורכבת מהמרכיבים הבאים:
    1. אנחנו כותבים את המבחן שלנו.
    2. אנחנו מריצים את המבחן, בין אם הוא עבר ובין אם לא (אנחנו רואים שהכל אדום - אל תתחרפן: ככה זה צריך להיות).
    3. אנו מוסיפים את הקוד שאמור לעמוד בבדיקה זו (הפעל את הבדיקה).
    4. אנחנו מחדשים את הקוד.
    בהתבסס על העובדה שבדיקות יחידות הן האלמנטים הקטנים ביותר בפירמידת האוטומציה של הבדיקות, TDD מבוסס עליהן. בעזרת בדיקות יחידה נוכל לבדוק את ההיגיון העסקי של כל מחלקה. BDD (התפתחות מונחה התנהגות) – התפתחות באמצעות התנהגות. גישה זו מבוססת על TDD. ליתר דיוק, הוא משתמש בדוגמאות הכתובות בשפה ברורה (בדרך כלל באנגלית) הממחישות את התנהגות המערכת עבור כל מי שמעורב בפיתוח. לא נעמיק במונח הזה, שכן הוא משפיע בעיקר על בודקים ואנליסטים עסקיים. Test Case - סקריפט המתאר את השלבים, התנאים והפרמטרים הספציפיים הדרושים לאימות יישום הקוד הנבדק. מתקן הוא מצב של סביבת הבדיקה הדרוש לביצוע מוצלח של השיטה הנבדקת. זוהי קבוצה קבועה מראש של אובייקטים והתנהגותם בתנאים שבהם נעשה שימוש.

    שלבי בדיקה

    המבחן מורכב משלושה שלבים:
    1. ציון הנתונים לבדיקה (מתקנים).
    2. שימוש בקוד הנבדק (קורא לשיטה הנבדקת).
    3. בדיקת התוצאות והשוואתן לאלו הצפויות.
    הכל על בדיקת יחידות: שיטות, מושגים, תרגול - 7כדי להבטיח מודולריות של הבדיקה, עליך להיות מבודד משכבות אחרות של היישום. זה יכול להיעשות באמצעות בדל, לעג ומרגלים. מוקס הם אובייקטים הניתנים להתאמה אישית (למשל, ספציפיים לכל בדיקה) ומאפשרים לך להגדיר ציפיות לקריאות למתודה בצורה של תגובות שאנו מתכננים לקבל. בדיקות ציפיות מבוצעות באמצעות קריאות ל-Mock objects. סטאבים - מספקים מענה קשיח לשיחות במהלך הבדיקה. הם יכולים גם לאחסן מידע על השיחה (לדוגמה, פרמטרים או מספר השיחות הללו). אלה נקראים לפעמים במונח שלהם - מרגל ( Spy ). לפעמים המונחים האלה בדל ולעג מתבלבלים: ההבדל הוא שסטאב לא בודק כלום, אלא רק מדמה מצב נתון . דומה הוא חפץ שיש לו ציפיות. לדוגמה, ששיטת מחלקה נתונה חייבת להיקרא מספר מסוים של פעמים. במילים אחרות, המבחן שלך לעולם לא יישבר בגלל בדל, אבל הוא עלול להישבר בגלל לעג.

    סביבות בדיקה

    אז עכשיו בואו ניגש לעניינים. קיימות מספר סביבות בדיקה (מסגרות) זמינות עבור Java. הפופולריים שבהם הם JUnit ו-TestNG. לסקירה שלנו, אנו משתמשים: הכל על בדיקת יחידות: שיטות, מושגים, תרגול - 8מבחן JUnit הוא שיטה הכלולה במחלקה המשמשת רק לבדיקה. מחלקה נקראת בדרך כלל זהה למחלקה שהיא בודקת עם +Test בסוף. לדוגמה, CarService → CarServiceTest. מערכת הבנייה של Maven כוללת באופן אוטומטי שיעורים כאלה באזור הבדיקה. למעשה, מחלקה זו נקראת כיתת מבחן. נעבור מעט על ההערות הבסיסיות: @Test - הגדרה של שיטה זו כשיטת בדיקה (למעשה, השיטה המסומנת בהערה זו היא מבחן יחידה). @Before - מסמן את השיטה שתתבצע לפני כל בדיקה. למשל, מילוי נתוני מבחן מחלקה, קריאת נתוני קלט וכו'. @After - ממוקם מעל השיטה שתיקרא לאחר כל מבחן (ניקוי נתונים, שחזור ערכי ברירת מחדל). @BeforeClass - ממוקם מעל השיטה - אנלוגי ל-@Before. אבל שיטה זו נקראת פעם אחת בלבד לפני כל המבחנים עבור מחלקה נתונה ולכן חייבת להיות סטטית. הוא משמש לביצוע פעולות כבדות יותר, כגון הרמת מסד נתונים בדיקה. @AfterClass הוא ההפך מ-@BeforeClass: מבוצע פעם אחת עבור מחלקה נתונה, אך מבוצע לאחר כל הבדיקות. משמש, למשל, לניקוי משאבים קבועים או ניתוק ממסד הנתונים. @התעלם - מציין שהשיטה למטה מושבתת ותתעלם ממנה בעת הפעלת בדיקות באופן כללי. הוא משמש במקרים שונים, למשל, אם שונתה שיטת הבסיס ולא היה זמן לבצע מחדש את הבדיקה עבורה. במקרים כאלה, רצוי להוסיף גם תיאור - @Ignore("Some description"). @Test (צפוי = Exception.class) - משמש לבדיקות שליליות. אלו בדיקות שבודקות כיצד מתנהגת שיטה במקרה של שגיאה, כלומר, הבדיקה מצפה שהשיטה תזרוק חריגה כלשהי. שיטה כזו מסומנת על ידי הערת @Test, אבל עם שגיאה לתפוס. @Test(timeout=100) - בודק שהשיטה מבוצעת תוך לא יותר מ-100 אלפיות שניות. @Mock - מחלקה משמשת מעל שדה כדי להגדיר אובייקט נתון כ-mock (זה לא מספריית Junit, אלא מ-Mockito), ואם אנחנו צריכים את זה, נגדיר את ההתנהגות של ה-mock במצב ספציפי , ישירות בשיטת הבדיקה. @RunWith(MockitoJUnitRunner.class) - השיטה ממוקמת מעל המחלקה. זה הכפתור להפעלת בדיקות בו. הרצים יכולים להיות שונים: לדוגמה, יש את הדברים הבאים: MockitoJUnitRunner, JUnitPlatform, SpringRunner וכו'). ב-JUnit 5, ההערה @RunWith הוחלפה בהערת @ExtendWith החזקה יותר. בואו נסתכל על כמה שיטות להשוואת תוצאות:
    • assertEquals(Object expecteds, Object actuals)- בודק אם האובייקטים המועברים שווים.
    • assertTrue(boolean flag)- בודק אם הערך שעבר מחזיר אמת.
    • assertFalse(boolean flag)- בודק אם הערך שעבר מחזיר false.
    • assertNull(Object object)– בודק אם האובייקט הוא null.
    • assertSame(Object firstObject, Object secondObject)- בודק אם הערכים שעברו מתייחסים לאותו אובייקט.
    • assertThat(T t, Matcher<T> matcher)- בודק אם t עומד בתנאי שצוין בהתאמה.
    יש גם טופס השוואה שימושי מ-assertj - assertThat(firstObject).isEqualTo(secondObject) כאן דיברתי על השיטות הבסיסיות, שכן השאר הן וריאציות שונות של האמור לעיל.

    תרגול בדיקות

    עכשיו בואו נסתכל על החומר לעיל באמצעות דוגמה ספציפית. נבדוק את השיטה לשירות - עדכון. לא נשקול את שכבת הדאו, מכיוון שהיא ברירת המחדל שלנו. בואו נוסיף התחלה למבחנים:
    <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-test</artifactId>
       <version>2.2.2.RELEASE</version>
       <scope>test</scope>
    </dependency>
    אז, מחלקת השירות:
    @Service
    @RequiredArgsConstructor
    public class RobotServiceImpl implements RobotService {
       private final RobotDAO robotDAO;
    
       @Override
       public Robot update(Long id, Robot robot) {
           Robot found = robotDAO.findById(id);
           return robotDAO.update(Robot.builder()
                   .id(id)
                   .name(robot.getName() != null ? robot.getName() : found.getName())
                   .cpu(robot.getCpu() != null ? robot.getCpu() : found.getCpu())
                   .producer(robot.getProducer() != null ? robot.getProducer() : found.getProducer())
                   .build());
       }
    }
    8 - משוך את האובייקט המעודכן ממסד הנתונים 9-14 - צור את האובייקט דרך ה-Builder, אם לאובייקט הנכנס יש שדה - הגדר אותו, אם לא - השאר את מה שיש במסד הנתונים ותראה את הבדיקה שלנו:
    @RunWith(MockitoJUnitRunner.class)
    public class RobotServiceImplTest {
       @Mock
       private RobotDAO robotDAO;
    
       private RobotServiceImpl robotService;
    
       private static Robot testRobot;
    
       @BeforeClass
       public static void prepareTestData() {
           testRobot = Robot
                   .builder()
                   .id(123L)
                   .name("testRobotMolly")
                   .cpu("Intel Core i7-9700K")
                   .producer("China")
                   .build();
       }
    
       @Before
       public void init() {
           robotService = new RobotServiceImpl(robotDAO);
       }
    1 - הרץ 4 שלנו - לבודד את השירות משכבת ​​ה-dao על ידי החלפת מדומה 11 - הגדר ישות בדיקה למחלקה (זו שבה נשתמש כאוגר בדיקה) 22 - הגדר אובייקט שירות אותו נבדוק
    @Test
    public void updateTest() {
       when(robotDAO.findById(any(Long.class))).thenReturn(testRobot);
       when(robotDAO.update(any(Robot.class))).then(returnsFirstArg());
       Robot robotForUpdate = Robot
               .builder()
               .name("Vally")
               .cpu("AMD Ryzen 7 2700X")
               .build();
    
       Robot resultRobot = robotService.update(123L, robotForUpdate);
    
       assertNotNull(resultRobot);
       assertSame(resultRobot.getId(),testRobot.getId());
       assertThat(resultRobot.getName()).isEqualTo(robotForUpdate.getName());
       assertTrue(resultRobot.getCpu().equals(robotForUpdate.getCpu()));
       assertEquals(resultRobot.getProducer(),testRobot.getProducer());
    }
    כאן אנו רואים חלוקה ברורה של הבדיקה לשלושה חלקים: 3-9 - הגדרת מכשירי 11 - ביצוע החלק הנבדק 13-17 - בדיקת התוצאות פרטים נוספים: 3-4 - הגדרת ההתנהגות של moka dao 5 - הגדרת מופע שנעדכן על תקן 11 שלנו - השתמשו בשיטה וקחו את המופע המתקבל 13 - בדקו שהוא לא אפס 14 - בדקו את מזהה התוצאה ואת ארגומנטי המתודה שצוינו 15 - בדקו אם השם עודכן 16 - תראו בתוצאה של מעבד 17 - מכיוון שלא קבענו זאת בשדה מופע העדכון, זה אמור להישאר זהה, בוא נבדוק את זה. הכל על בדיקת יחידות: שיטות, מושגים, תרגול - 9בואו להשיק: הכל על בדיקת יחידות: שיטות, מושגים, תרגול - 10הבדיקה ירוקה, אפשר לנשוף)) אז בואו נסכם: בדיקה משפרת את איכות הקוד והופכת את תהליך הפיתוח לגמיש ואמין יותר. תארו לעצמכם כמה מאמץ נצטרך להשקיע בעת עיצוב מחדש של תוכנה עם מאות קבצי כיתה. ברגע שנכתבו מבחני יחידה לכל הכיתות הללו, נוכל לבצע מחדש בביטחון. והכי חשוב, זה עוזר לנו למצוא בקלות שגיאות במהלך הפיתוח. חבר'ה, זה הכל בשבילי היום: צרפו לייקים, כתבו תגובות))) הכל על בדיקת יחידות: שיטות, מושגים, תרגול - 11
הערות
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION