首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

JUnit 4/5 單元測試實戰:從入門到最佳實踐完整指南

     編輯:Java 相關知識

前言

JUnit 是 Java 生態事實標準的測試框架。本文涵蓋 JUnit 4 (廣泛存量) 與 JUnit 5 (現代標準) 核心用法、參數化測試、斷言、生命週期、Mock 整合、測試組織與 CI 整合要點。

一、 環境準備

Maven 依賴 (JUnit 5 推薦)

<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 4JUnit 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

三、 JUnit 5 進階特性實戰

1. 參數化測試 —— 消除重複代碼

@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)
    );
}

2. 動態測試與嵌套類別

@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)))));
}

3. 強大的斷言 API (AssertJ 風格)


// 基礎
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());

4. 條件執行與標籤過濾

@EnabledOnOs(OS.LINUX)
@EnabledOnJre(JRE.JAVA_11)
@Tag("integration") // mvn test -Dgroups=integration
@Test void linuxOnlyTest() { ... }

四、 測試組織與 Suite (JUnit 5 方式)

JUnit 5 不再使用 @RunWith(Suite.class),改用 @Suite (需 junit-platform-suite 依賴):

@Suite
@SelectPackages("com.myapp.service")
@IncludeTags("fast")
@ExcludeTags("slow")
class FastServiceSuite {}

五、 Mockito 整合測試雙 (Mock/Stub/Spy)


@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));
    }
}

六、 最佳實踐清單

  1. 命名規範:方法名_條件_預期結果 (如 withdraw_insufficientBalance_throwsException)。
  2. AAA 模式:Arrange (準備) → Act (執行) → Assert (驗證)。
  3. 單一職責:一個測試只驗證一個行為/邊界。
  4. 快速、獨立、可重複:無外部依賴 (DB/網路) → 用 Mock/Embedded DB/Testcontainers。
  5. 測試金字塔:大量單元測試 + 適量整合測試 + 少量 E2E 測試。
  6. 覆蓋率非目標:關注關鍵路徑/邊界/異常分支,而非追求 100% 行覆蓋。
  7. 測試代碼即文檔:清晰表達業務規則,新人閱讀測試即懂功能。

七、 CI/CD 整合

  • Maven: mvn test 自動執行 src/test 下測試。
  • Gradle: gradle test。
  • Surefire/Failsafe 插件配置並行執行、分組、報告格式 (JUnit XML → Jenkins/GitLab CI 解析)。
  • JaCoCo 產生覆蓋率報告,設定門檻失效建構。

總結

JUnit 5 模組化架構、豐富擴展點、現代 Java 特性 (Lambda/Stream) 支持,大幅提升測試表達力。掌握參數化、動態測試、條件執行、斷言 API,配合 Mockito 與測試金字塔策略,即能構建高可信、可維護的自動化測試體系。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved