مهاجرت از 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>
منبع: