دانستنی‌ها

مهاجرت از JUnit 4 به JUnit5

در این مطلب با مروری بر تفاوت‌های JUnit 4 و JUnit 5 نحوه مهاجرت به آخرین نسخه این کتابخانه را توضیح می‌دهیم.

مزایای JUnit 5

برای بررسی مزایای نسخه جدید، مشکلات نسخه قبلی را مطرح و یادآوری می‌کنیم. JUnit 4  محدودیت‌های روشنی دارد:

  • کل کتابخانه در یک فایل jar قرار دارد و حتی اگر یک قابلیت خاص را نیاز داشته باشید لازم است کل کتابخانه را import کنید. در JUnit 5 ریزدانگی بیشتری داریم و می‌توانیم بخش مورد نیاز را import کنیم.
  • تنها یک test runner  می‌تواند در زمان، تست‌ها را در JUnit 4 اجرا کند (مثلا SpringJUnit4ClassRunner یا Parameterized) اما JUnit5 اجازه می‌دهد چندین runner همزمان کار کنند.
  • کتابخانه JUnit4 تنها تا جاوا ۷ پیشروی داشته و بسیاری از ویژگی های جاوا ۸ را از دست داده است اما JUnit5 به خوبی می‌تواند از ویژگی‌های جاوا ۸ استفاده کند.

ایده‌ای که پشت JUnit5 بوده است نوشتن مجدد JUnit4 برای حل بسیاری از این مشکلات است.

تفاوت‌ها

کتابخانه JUnit4 به ماژول‌هایی شکسته شده که JUnit5  را تشکیل می‌دهند.

۱. JUnit Platform: این ماژول تمام چارچوب‌هایی که ممکن است برای اجرا، ریکاوری یا گزارش دادن نیاز داشته باشیم را شامل می‌شود.

۲. JUnit Vintage: این ماژول backward compatibility با JUnit4 و حتی JUnit3 را فراهم می‌آورد.

Annotationها

JUnit 5 با تغییرات مهمی در annotationها آمده است. مهمترین آن این است که دیگر نمی‌توان از @Test برای مشخص کردن انتظارات استفاده کرد.

مثلا در JUnit 4 داشتیم:

@Test(expected = Exception.class)
public void shouldRaiseAnException() throws Exception {
    // ...
}

و حالا از متد assertThrows استفاده می‌کنیم

public void shouldRaiseAnException() throws Exception {
    Assertions.assertThrows(Exception.class, () -> {
        //...
    });
}

در JUnit4 از timeout استفاده می‌کردیم

@Test(timeout = 1)
public void shouldFailBecauseTimeout() throws InterruptedException {
    Thread.sleep(10);
}

اما الان از assertTimeout استفاده می‌کنیم.

@Test
public void shouldFailBecauseTimeout() throws InterruptedException {
    Assertions.assertTimeout(Duration.ofMillis(1), () -> Thread.sleep(10));
}

سایر annotationهایی که تغییر کرده اند:

•@Before به @BeforeEach تغییر کرده است

•@After به @AfterEach تغییر کرده است

•@BeforeClass به @BeforeAll تغییر کرده است

•@AfterClass به @AfterAll تغییر کرده است.

•@Ignore به @Disabled تغییر کرده است.

 Assertionها

اکنون می‌توانیم پیام‌های assertion را در یک لامبدا در JUnit 5 بنویسیم که lazy evaluation را فراهم می‌کند یا به عبارتی ساخت پیام‌های پیچیده را تا زمانی که نیاز است به تعویق می‌اندازد.

@Test
public void shouldFailBecauseTheNumbersAreNotEqual_lazyEvaluation() {
    Assertions.assertTrue(
      2 == 3, 
      () -> "Numbers " + 2 + " and " + 3 + " are not equal!");
}

به علاوه می‌توان assertionها را گروه بندی کرد.

@Test
public void shouldAssertAllTheGroup() {
    List<Integer> list = Arrays.asList(1, 2, 4);
    Assertions.assertAll("List is not incremental",
        () -> Assertions.assertEquals(list.get(0).intValue(), 1),
        () -> Assertions.assertEquals(list.get(1).intValue(), 2),
        () -> Assertions.assertEquals(list.get(2).intValue(), 3));
}

assumptionها

کلاس جدید assumption الان در org.junit.jupiter.api.Assumptions قرار دارد. البته نسخه جدید به طور کامل از توابع نسخه‌های قبلی پشتیبانی می‌کند و البته توابع جدیدی را هم برای سناریو‌های خاص مطرح می‌نماید.

@Test
public void whenEnvironmentIsWeb_thenUrlsShouldStartWithHttp() {
    assumingThat("WEB".equals(System.getenv("ENV")),
      () -> {
          assertTrue("http".startsWith(address));
      });
}

برچسب گذاری و فیلتر کردن

در JUnit 4  می‌توانستیم تست ها را با @Category گروه بندی کنیم. در JUnit 5 این annotation با @Tag جایگزین شده است.

@Tag("annotations")
@Tag("junit5")
@RunWith(JUnitPlatform.class)
public class AnnotationTestExampleTest {
    /*...*/
}

میتوان برچسب ها را با استفاده از maven-surefire-plugin در پروژه include/excluse کرد.

<build>
    <plugins>
        <plugin>
            <artifactId>maven-surefire-plugin</artifactId>
            <configuration>
                <properties>
                    <includeTags>junit5</includeTags>
                </properties>
            </configuration>
        </plugin>
    </plugins>
</build>

 annotationهای جدید برای اجرای تست‌ها

سابقا @RunWith برای یکپارچه سازی تست با فریم ورک‌های دیگر یا تغییر کلی جریان اجرا در تست کیس‌ها در JUnit 4 استفاده می‌شد.

به عنوان مثال :

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
  {"/app-config.xml", "/test-data-access-config.xml"})
public class SpringExtensionTest {
    /*...*/
}

الان در JUnit 5

@ExtendWith(SpringExtension.class)
@ContextConfiguration(
  { "/app-config.xml", "/test-data-access-config.xml" })
public class SpringExtensionTest {
    /*...*/
}

annotationهای جدید قوانین تست

در JUnit 4 انوتیشن‌های @rule و @ClassRule برای افزودن قابلیت‌های جدید به تست ها استفاده می‌شد.

در JUnit 5 ما می‌توانیم منطق مشابهی را با استفاده از @ExtendWith ایجاد کنیم.

برای مثال فرض کنید یک قانون خاص در JUnit 4 برای نوشتن log قبل و بعد از تست داریم

public class TraceUnitTestRule implements TestRule {
  
    @Override
    public Statement apply(Statement base, Description description) {
        return new Statement() {
            @Override
            public void evaluate() throws Throwable {
                // Before and after an evaluation tracing here 
                ...
            }
        };
    }
}

و در یک مجموعه تست این چنین پیاده می‌کنیم

@Rule
public TraceUnitTestRule traceRuleTests = new TraceUnitTestRule();

در JUnit5 به این شکل به سادگی می توان نوشت

public class TraceUnitExtension implements AfterEachCallback, BeforeEachCallback {
 
    @Override
    public void beforeEach(TestExtensionContext context) throws Exception {
        // ...
    }
 
    @Override
    public void afterEach(TestExtensionContext context) throws Exception {
        // ...
    }
}

با استفاده از قابلیت AfterEachCallback و BeforeEachCallback در JUnit 5 این کار به سادگی انجام می‌شود.

@RunWith(JUnitPlatform.class)
@ExtendWith(TraceUnitExtension.class)
public class RuleExampleTest {
  
    @Test
    public void whenTracingTests() {
        /*...*/
    }
}

JUnit 5 Vintage

JUnit Vintage برای مهاجرت  از نسخه ۳ و ۴ کمک می‌کند.

می توانیم آن را با وارد کردن Junit Vintage Engine استفاده کنیم.

<dependency>
    <groupId>org.junit.vintage</groupId>
    <artifactId>junit-vintage-engine</artifactId>
    <version>${junit5.vintage.version}</version>
    <scope>test</scope>
</dependency>

 

منبع:

http://www.baeldung.com/junit-5-migration

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا