מדריך JUnit5

מדריך ל-JUnit5

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

ארכיטקטורת JUnit5

פלטפורמת JUnit

  • משיקה מסגרות בדיקה על ה-JVM
  • כוללת את ממשק ה-TestEngine המשמש לבניית מסגרת בדיקה הרצה על הפלטפורמה של JUnit

JUnit Jupiter

  • משלבת מודל תכנות חדש לכתיבת בדיקות ומודל הרחבה להרחבות
  • הוספת הערות חדשות כמו @BeforeEach, @AfterEach, @AfterAll, @BeforeAll וכו'

JUnit Vintage

  • מספק תמיכה בביצוע מבחני JUnit גרסה 3 ו-4 קודמים על פלטפורמה זו

JUnit Maven Dependencies

כדי ליישם מבחני JUnit5 בפרויקט, יש להוסיף את התלות הבאה לקובץ pom.xml של הפרויקט:

  • ספריית JUnit 5
<dependency>
     <groupId>org.junit.jupiter</groupId>
     <artifactId>junit-jupiter-engine</artifactId>
     <version>5.1.1</version>
     <scope>test</scope>
</dependency>
<dependency>
     <groupId>org.junit.platform</groupId>
     <artifactId>junit-platform-runner</artifactId>
     <version> 1.1.1</version>
     <scope>test</scope>
</dependency>
  • ספק של JUnit5 maven surefire כדי לבצע את מבחני היחידה במקום בו הסביבת פיתוח אינה תומכת ב-JUnit5 (אם הסביבה פיתוח תומכת, אז נקודה זו אינה נדרשת)
<plugin>
     <artifactId>maven-surefire-plugin</artifactId>
     <version>2.19.1</version>
     <dependencies>
          <dependency>
               <groupId>org.junit.platform</groupId>
               <artifactId>junit-platform-surefire-provider</artifactId>
               <version>1.0.2</version>
          </dependency>
     </dependencies>
</plugin>

תכונות חדשות של JUnit5

נדרש Java 8 או גרסה גבוהה יותר בזמן ריצה. אך ניתן עדיין לבדוק קוד שנקודם זה נקודות גרסה של Java קודמות. קיימות מגוון של תכונות חדשות שהוצגו בו.

אננוטציות JUnit

רשומות למטה הן כמה מהאננוטציות הנפוצות המסופקות ב-JUnit:

Annotation Description
@Test Denotes a test method
@DisplayName Declares a custom display name for the test class or test method
@BeforeEach Denotes that the annotated method should be executed before each test method
@AfterEach Denotes that the annotated method should be executed after each test method
@BeforeAll Denotes that the annotated method should be executed before all test methods
@AfterAll Denotes that the annotated method should be executed after all test methods
@Disable Used to disable a test class or test method
@Nested Denotes that the annotated class is a nested, non-static test class
@Tag Declare tags for filtering tests
@ExtendWith Register custom extensions
package com.journaldev;

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

public class JUnit5Sample1Test {

  @BeforeAll
  static void beforeAll() {
    System.out.println("**--- Executed once before all test methods in this class ---**");
  }

  @BeforeEach
  void beforeEach() {
    System.out.println("**--- Executed before each test method in this class ---**");
  }

  @Test
  void testMethod1() {
    System.out.println("**--- Test method1 executed ---**");
  }

  @DisplayName("Test method2 with condition")
  @Test
  void testMethod2() {
    System.out.println("**--- Test method2 executed ---**");
  }

  @Test
  @Disabled("implementation pending")
  void testMethod3() {
	  System.out.println("**--- Test method3 executed ---**");
  }

  @AfterEach
  void afterEach() {
    System.out.println("**--- Executed after each test method in this class ---**");
  }

  @AfterAll
  static void afterAll() {
    System.out.println("**--- Executed once after all test methods in this class ---**");
  }


}

ניתן להריץ את מחלקת המבחן של JUnit למעלה ב- Eclipse -> Run As -> JUnit Test.

אסרציות JUnit

כל שיטת בדיקה חייבת להיערך נגד תנאי אמת באמצעות אסרציות כדי שהבדיקה תוכל להמשיך להרץ. אסרציות JUnit Jupiter נמצאות במחלקת org.junit.jupiter.api.Assertions. כל השיטות הן סטטיות.

Assertion Description
assertEquals(expected, actual) Fails when expected does not equal actual
assertFalse(expression) Fails when expression is not false
assertNull(actual) Fails when actual is not null
assertNotNull(actual) Fails when actual is null
assertAll() Group many assertions and every assertion is executed even if one or more of them fails
assertTrue(expression) Fails if expression is not true
assertThrows() Class to be tested is expected to throw an exception
@Test
void testAssertEqual() {
	 assertEquals("ABC", "ABC");
	 assertEquals(20, 20, "optional assertion message");
	 assertEquals(2 + 2, 4);
}

@Test
void testAssertFalse() {
	 assertFalse("FirstName".length() == 10);
	 assertFalse(10 > 20, "assertion message");
}

@Test
void testAssertNull() {
     String str1 = null;
	 String str2 = "abc";
	 assertNull(str1);
	 assertNotNull(str2);	
}

@Test
void testAssertAll() {
	 String str1 = "abc";
	 String str2 = "pqr";
	 String str3 = "xyz";
	 assertAll("numbers",
	      () -> assertEquals(str1,"abc"),
		  () -> assertEquals(str2,"pqr"),
		  () -> assertEquals(str3,"xyz")
	 );
	 //בטל את התגובה לקוד למטה והבן כל ביצוע של assert
     /*assertAll("numbers",
		  () -> assertEquals(str1,"abc"),
		  () -> assertEquals(str2,"pqr1"),
		  () -> assertEquals(str3,"xyz1")
	 );*/
}

@Test
void testAssertTrue() {
	 assertTrue("FirstName".startsWith("F"));
	 assertTrue(10  {
	      throw new IllegalArgumentException("Illegal Argument Exception occured");
	 });
	 assertEquals("Illegal Argument Exception occured", exception.getMessage());
}

ייבואים של JUnit5

מחלקות הבדיקה שלו דורשות הייבוא של org.junit.jupiter.api.Test ולא org.junit.Test. גם, שיטות הבדיקה אינן צריכות להיות ציבוריות ובחבילה מקומית.

import org.junit.jupiter.api.Test;

הנחיות JUnit5

הנחיות הן שיטות סטטיות במחלקת org.junit.jupiter.api.Assumptions. הן תפעלו בדיקה רק כאשר התנאי המסוים יתקיים, אחרת הבדיקה תבוטל. הבדיקה שנבטלה לא תגרום לכשל בבניית המיזם. כאשר הנחה נכשלת, ישוב org.opentest4j.TestAbortedException והבדיקה תדלג.

Assumptions Description
assumeTrue Execute the body of lamda when the positive condition hold else test will be skipped
assumeFalse Execute the body of lamda when the negative condition hold else test will be skipped
assumingThat Portion of the test method will execute if an assumption holds true and everything after the lambda will execute irrespective of the assumption in assumingThat() holds
@Test
void testAssumeTrue() {
     boolean b = 'A' == 'A';
     assumeTrue(b);
     assertEquals("Hello", "Hello");
}

@Test
@DisplayName("test executes only on Saturday")
public void testAssumeTrueSaturday() {
     LocalDateTime dt = LocalDateTime.now();
     assumeTrue(dt.getDayOfWeek().getValue() == 6);
     System.out.println("further code will execute only if above assumption holds true");
}

@Test
void testAssumeFalse() {
     boolean b = 'A' != 'A';
     assumeFalse(b);
     assertEquals("Hello", "Hello");
}

@Test
void testAssumeFalseEnvProp() {
     System.setProperty("env", "prod");
     assumeFalse("dev".equals(System.getProperty("env")));
     System.out.println("further code will execute only if above assumption hold");
}

@Test
void testAssumingThat() {
     System.setProperty("env", "test");
     assumingThat("test".equals(System.getProperty("env")),
          () -> {
               assertEquals(10, 10);
               System.out.println("perform below assertions only on the test env");
               });

     assertEquals(20, 20);
     System.out.println("perform below assertions on all env");
}

מחלקות בדיקה מקוננות ב-JUnit

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

@BeforeAll
static void beforeAll() {
     System.out.println("**--- JUnit5Sample4Test :: beforeAll :: Executed once before all test methods ---**");
}
 
@BeforeEach
void beforeEach() {
	 System.out.println("**--- JUnit5Sample4Test :: beforeEach :: Executed before each test method ---**");
}

@AfterEach
void afterEach() {
	 System.out.println("**--- JUnit5Sample4Test :: afterEach :: Executed after each test method ---**");
}

@AfterAll
static void afterAll() {
	 System.out.println("**--- JUnit5Sample4Test :: afterAll :: Executed after all test method ---**");
}
 
     @Nested
     class InnerClass {
 
          @BeforeEach
          void beforeEach() {
               System.out.println("**--- InnerClass :: beforeEach :: Executed before each test method ---**");
          }
 
          @AfterEach
          void afterEach() {
        	   System.out.println("**--- InnerClass :: afterEach :: Executed after each test method ---**");
          }
 
          @Test
          void testMethod1() {
        	   System.out.println("**--- InnerClass :: testMethod1 :: Executed test method1 ---**");
          }
 
          @Nested
          class InnerMostClass {
 
               @BeforeEach
               void beforeEach() {
                    System.out.println("**--- InnerMostClass :: beforeEach :: Executed before each test method ---**");
               }
 
               @AfterEach
               void afterEach() {
            	    System.out.println("**--- InnerMostClass :: afterEach :: Executed after each test method ---**");
               }
 
               @Test
               void testMethod2() {
            	    System.out.println("**--- InnerMostClass :: testMethod2 :: Executed test method2 ---**");
               }
        }
    }

חריגת בדיקת JUnit

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

Throwable exception = assertThrows(IllegalArgumentException.class, () -> {
     throw new IllegalArgumentException("Illegal Argument Exception occured");
});
assertEquals("Illegal Argument Exception occured", exception.getMessage());

ביצוע בדיקות JUnit

הבדיקות היחידה ניתן להפעיל בכמה דרכים, שתי הדרכים הן כדלקמן:

  • השתמשו בסביבת הפיתוח Eclipse IDE Oxygen.3a (4.7.3a) Release ופתחו את קובץ הבדיקות שברצונכם להפעיל. לחצו עם העכבר ימינה על הקובץ ובחרו אפשרות "הרץ כ" ואז "בדיקת JUnit"
  • השתמשו בפקודת mvn test בפקודה של Windows

סיכום

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

ניתן להוריד את פרויקט הדוגמה המלא מה-מאגר הקוד שלנו ב-GitHub.

Source:
https://www.digitalocean.com/community/tutorials/junit5-tutorial