JUnit 5 เป็นเวอร์ชันล่าสุดของ Testing Framework ยอดนิยมสำหรับ JVM รองรับ Java 8+ และมี Annotations ใหม่หลายตัว บทความนี้ครอบคลุมทุก Annotations หลักพร้อมโค้ดตัวอย่าง
Setup: Maven Dependency
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.10.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>5.10.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.10.2</version>
<scope>test</scope>
</dependency>
</dependencies>
Annotations หลัก
@Test - เมธอดทดสอบพื้นฐาน
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class JUnit5Test {
@Test
void helloJUnit5() {
assertEquals(10, 5 + 5);
}
}
✅ การเปลี่ยนแปลงจาก JUnit 4: Class และ Method ไม่ต้องเป็น public แล้ว
@ParameterizedTest - ทดสอบหลายค่า
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import static org.junit.jupiter.api.Assertions.*;
class JUnit5Test {
@ParameterizedTest
@ValueSource(strings = {"cali", "bali", "dani"})
void endsWithI(String str) {
assertTrue(str.endsWith("i"));
}
}
Argument Sources อื่นๆ: @EnumSource, @MethodSource, @CsvSource, @CsvFileSource
@RepeatedTest - ทดสอบซ้ำหลายครั้ง
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.RepeatedTest;
import org.junit.jupiter.api.RepetitionInfo;
import org.junit.jupiter.api.TestInfo;
import static org.junit.jupiter.api.Assertions.*;
class JUnit5Test {
@RepeatedTest(value = 5, name = "{displayName} {currentRepetition}/{totalRepetitions}")
@DisplayName("Repeating Test")
void customDisplayName(RepetitionInfo repInfo, TestInfo testInfo) {
System.out.println(testInfo.getDisplayName() + " -> " + repInfo.getCurrentRepetition());
// Test logic here
}
}
✅ เหมาะสำหรับ: Flaky tests, UI testing (Selenium), Load testing
@DisplayName - ชื่อทดสอบที่อ่านง่าย
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInfo;
@DisplayName("User Service Tests")
class UserServiceTest {
@Test
@DisplayName("Should return user when valid ID provided")
void testFindById() { }
@Test
@DisplayName("Should throw exception when user not found")
void testNotFound(TestInfo testInfo) {
System.out.println(testInfo.getDisplayName());
}
}
@BeforeEach / @AfterEach - Setup/Teardown ต่อ Test
import org.junit.jupiter.api.*;
class LifecycleTest {
@BeforeEach
void setUp(TestInfo testInfo) {
System.out.println("Before: " + testInfo.getTestMethod().get().getName());
}
@Test void firstTest() { System.out.println("Test 1"); }
@Test void secondTest() { System.out.println("Test 2"); }
@AfterEach
void tearDown(TestInfo testInfo) {
System.out.println("After: " + testInfo.getTestMethod().get().getName());
}
}
// Output:
// Before: firstTest
// Test 1
// After: firstTest
// Before: secondTest
// Test 2
// After: secondTest
@BeforeAll / @AfterAll - Setup/Teardown ครั้งเดียวสำหรับ Class
import org.junit.jupiter.api.*;
class LifecycleTest {
@BeforeAll
static void initAll() {
System.out.println("Run once before all tests");
}
@AfterAll
static void tearDownAll() {
System.out.println("Run once after all tests");
}
@Test void test1() { System.out.println("Test 1"); }
@Test void test2() { System.out.println("Test 2"); }
}
// Output:
// Run once before all tests
// Test 1
// Test 2
// Run once after all tests
⚠️ สำคัญ: @BeforeAll และ @AfterAll ต้องเป็น
staticmethod
@Tag - กรอง Test (Filtering)
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
@Tag("smoke")
class JUnit5Test {
@Test @Tag("login") void validLoginTest() { }
@Test @Tag("search") void searchTest() { }
}
รันเฉพาะ smoke test: mvn test -Dgroups=smoke
@Disabled - ปิดการทดสอบ
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
@Disabled // ปิดทั้ง Class
class DisabledClassDemo {
@Test void testWillBeSkipped() { }
}
class DisabledTestsDemo {
@Disabled @Test void testWillBeSkipped() { } // ปิดแค่ Method
@Test void testWillBeExecuted() { }
}
Annotations เพิ่มเติมที่น่าสนใจ
| Annotation | คำอธิบาย |
|---|---|
| @Nested | Nested test classes (Inner class) |
| @TestFactory | Dynamic Tests (สร้าง Test Runtime) |
| @TestTemplate | Template for test cases |
| @ExtendWith | Register Extensions (Mockito, Spring, etc.) |
| @Timeout | Set timeout for test |
| @Order | ระบุลำดับการรัน Test (กับ @TestMethodOrder) |
Assertions ที่ใช้บ่อย
import static org.junit.jupiter.api.Assertions.*;
assertEquals(expected, actual);
assertNotEquals(unexpected, actual);
assertTrue(condition);
assertFalse(condition);
assertNull(object);
assertNotNull(object);
assertSame(expected, actual); // same reference
assertThrows(Exception.class, () -> { ... });
assertAll("group", () -> assertTrue(...), () -> assertEquals(...)); // Grouped assertions