دليل JUnit5

برنامج تعليمي لـ JUnit5

في هذا البرنامج التعليمي لـ JUnit، سنقدم أساسيات JUnit5 وميزاته الجديدة باستخدام أمثلة. في عالم الجافا، JUnit هو أحد الأطر الشهيرة المستخدمة لتنفيذ اختبارات الوحدات ضد رمز الجافا. يساعد JUnit في المقام الأول المطورين على اختبار رمزهم على الـ JVM بأنفسهم.

هندسة JUnit5

منصة JUnit

  • تطلق أطر اختبار على الـ JVM
  • لديها واجهة برمجة تطبيقات لبناء إطار اختبار يعمل على منصة JUnit

جوبيتر JUnit

  • مزيج من النموذج البرمجي الجديد لكتابة الاختبارات والنموذج التوسيعي للامتدادات
  • إضافة تعليقات جديدة مثل `التعليقات` مثل `@BeforeEach`, `@AfterEach`, `@AfterAll`, `@BeforeAll` إلخ. `

JUnit Vintage

  • ` توفير الدعم لتنفيذ اختبارات الإصدارات السابقة لـ JUnit 3 و 4 على هذا الإصدار الجديد `

تبعيات JUnit Maven

` لتنفيذ حالات الاختبار القائمة على 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

أدناه قائمة ببعض التعليقات الشائعة المستخدمة فيه:

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 السابق في إيكلبس -> تشغيل كـ -> اختبار JUnit.

تحقيقات 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")
	 );
	 // ألغ تعليق الكود أدناه وفهم تنفيذ كل تأكد
     /*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 وافتح الملف الذي يجب تنفيذه. انقر بزر الماوس الأيمن على الملف واختر الخيار “Runs As” ثم JUnit Test
  • استخدم أمر mvn test على سطر الأوامر في نظام Windows

الملخص

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

يمكنك تنزيل المشروع النموذجي الكامل من مستودع GitHub الخاص بنا.

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