JUnit 是 Java 生態事實標準的測試框架。本文涵蓋 JUnit 4 (廣泛存量) 與 JUnit 5 (現代標準) 核心用法、參數化測試、斷言、生命週期、Mock 整合、測試組織與 CI 整合要點。
<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-engine</artifactId>
<version>5.10.2</version>
<scope>test</scope>
</dependency>
JUnit 4 相容:加入 junit-vintage-engine 即可同時運行舊測試。
| 功能 | JUnit 4 | JUnit 5 (Jupiter) |
|---|---|---|
| 測試方法 | @Test | @Test (org.junit.jupiter.api) |
| 類初始化 (一次) | @BeforeClass (static) | @BeforeAll (static) |
| 每測試前初始化 | @Before | @BeforeEach |
| 每測試後清理 | @After | @AfterEach |
| 類清理 (一次) | @AfterClass (static) | @AfterAll (static) |
| 忽略測試 | @Ignore | @Disabled |
| 預期異常 | @Test(expected=...) | assertThrows() 斷言 API |
| 超時 | @Test(timeout=...) | assertTimeout() 斷言 API |
@ParameterizedTest
@ValueSource(strings = {"", " ", "\t", "\n"})
@NullSource
void isBlank_ShouldReturnTrue(String input) {
assertTrue(StringUtils.isBlank(input));
}
@ParameterizedTest
@CsvSource({"1, 1, 2", "2, 3, 5", "10, 20, 30"})
void addTest(int a, int b, int expected) {
assertEquals(expected, Calculator.add(a, b));
}
// 自定義參數提供者
@ParameterizedTest
@MethodSource("provideTestCases")
void complexTest(UserInput input, Result expected) { ... }
static Stream<Arguments> provideTestCases() {
return Stream.of(
Arguments.of(new UserInput("a"), Result.OK),
Arguments.of(new UserInput(""), Result.ERROR)
);
}
@Nested
class AddTests {
@Test void addPositive() { ... }
@Test void addNegative() { ... }
}
@TestFactory
DynamicNode dynamicTests() {
return DynamicContainer.dynamicContainer("Dynamic Add",
Stream.of(1,2,3).map(i -> DynamicTest.dynamicTest("add " + i, () -> assertEquals(i+1, calc.add(1,i)))));
}
// 基礎
assertEquals(expected, actual);
assertNotNull(obj);
assertTrue(condition);
// 組合與豐富訊息
assertAll("user",
() -> assertEquals("John", user.getFirstName()),
() -> assertEquals("Doe", user.getLastName())
);
// 異常斷言
assertThrows(IllegalArgumentException.class, () -> service.process(null));
// 超時斷言
assertTimeout(Duration.ofMillis(100), () -> slowOperation());
@EnabledOnOs(OS.LINUX)
@EnabledOnJre(JRE.JAVA_11)
@Tag("integration") // mvn test -Dgroups=integration
@Test void linuxOnlyTest() { ... }
JUnit 5 不再使用 @RunWith(Suite.class),改用 @Suite (需 junit-platform-suite 依賴):
@Suite
@SelectPackages("com.myapp.service")
@IncludeTags("fast")
@ExcludeTags("slow")
class FastServiceSuite {}
@ExtendWith(MockitoExtension.class)
class OrderServiceTest {
@Mock PaymentGateway gateway;
@InjectMocks OrderService service;
@Test
void placeOrder_Success() {
when(gateway.charge(any())).thenReturn(new ChargeResult(true, "txn_123"));
OrderResult result = service.placeOrder(new Order(...));
assertTrue(result.isSuccess());
verify(gateway).charge(argThat(c -> c.getAmount() > 0));
}
}
方法名_條件_預期結果 (如 withdraw_insufficientBalance_throwsException)。mvn test 自動執行 src/test 下測試。gradle test。JUnit 5 模組化架構、豐富擴展點、現代 Java 特性 (Lambda/Stream) 支持,大幅提升測試表達力。掌握參數化、動態測試、條件執行、斷言 API,配合 Mockito 與測試金字塔策略,即能構建高可信、可維護的自動化測試體系。