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

JUnit 4 異常測試完全指南:從 try-catch 到 @Rule ExpectedException 進階實戰

     編輯:Java 相關知識

JUnit 核心概念與基本架構

JUnit 是由 Erich Gamma 與 Kent Beck 開發的回歸測試框架,屬於白箱測試(程式設計師測試)。核心用法:繼承 TestCase、以 testXXX() 命名測試方法、引入 junit.framework.*。

基礎範例

// 被測類
public class MathTool {
  public static int gcd(int a, int b) {
    int r;
    while (b != 0) { r = a % b; a = b; b = r; }
    return a;
  }
}

// 測試類
public class MathToolTest extends TestCase {
  public MathToolTest(String name) { super(name); }
  public void testGcd() { assertEquals(5, MathTool.gcd(10, 5)); }
}

傳統 try-catch 異常測試模式與缺陷

@Test
public void testBizException() {
  try {
    Password.validate("123");
    fail("No exception thrown."); // 關鍵:無異常時測試必須失敗
  } catch (Exception ex) {
    assertTrue(ex instanceof BizException);
    assertTrue(ex.getMessage().contains("error"));
  }
}

痛點:

  • 樣板代碼多,可讀性差
  • 斷言失敗時堆疊資訊不清晰,難以定位是異常型別錯誤還是訊息內容不符
  • 易忘記 fail() 導致漏測

JUnit 4 註解式異常測試:@Test(expected)

@Test(expected = BizException.class)
public void testBizException() {
  Password.validate(null);
}

優點:簡潔、可讀性高、框架層面保證異常拋出。

限制:僅能驗證異常類別,無法檢查異常訊息內容。

進階方案:@Rule ExpectedException (JUnit 4.7+)

核心用法

@Rule
public ExpectedException expectedEx = ExpectedException.none();

@Test
public void testBizException() throws InvalidPasswordException {
  expectedEx.expect(BizException.class);           // 驗證異常類別
  expectedEx.expectMessage("required");            // 驗證訊息包含子字串
  // expectedEx.expectMessage(Matcher matcher);    // 或使用 Hamcrest Matcher 正則
  Password.validate("");
}

五大關鍵注意事項

  1. ExpectedException 欄位必須為 public 且標註 @Rule
  2. 不可同時使用 @Test(expected=...) 與 expectedEx.expect...(),會衝突
  3. expectMessage() 參數支援 Matcher (正則) 或 String (子字串)
  4. 被測方法呼叫必須在 expect 設定之後,否則異常被提前拋出未被攔截
  5. 測試方法可直接拋出被測方法的檢查型異常,無需捕獲

三種方案失敗訊息對比

方案無異常時異常類別錯誤異常訊息不符
try-catchAssertionError: No exception thrown.AssertionError 無具體上下文
@Test(expected)AssertionError: Expected exception: cc.test.BizException
@Rule ExpectedExceptionExpected: (exception with message a string containing "YES. required" and an instance of BizException) got: [實際異常詳情]

結論:@Rule ExpectedException 提供最豐富的失敗上下文,包含期望異常型別、期望訊息模式、實際獲得異常型別與訊息,極大提升除錯效率。

實戰模式:同一方法拋出同類異常但訊息不同

// 業務邏輯
public void validate(String pwd) {
  if (pwd.length() < 6) throw new BizException("Password must contains at least 6 letters.");
  if (pwd.length() > 15) throw new BizException("Password length less than 15 letters");
}

// 測試案例
@Test
public void testTooShort() {
  expectedEx.expect(BizException.class);
  expectedEx.expectMessage("at least 6");
  Password.validate("123");
}

@Test
public void testTooLong() {
  expectedEx.expect(BizException.class);
  expectedEx.expectMessage("less than 15");
  Password.validate("1234567890123456");
}

最佳實踐總結

  1. 簡單驗證異常型別:使用 @Test(expected = XxxException.class)
  2. 需驗證異常訊息:使用 @Rule ExpectedException + expectMessage()
  3. 複雜訊息模式:搭配 Hamcrest CoreMatchers.containsString()、matchesPattern()
  4. 避免 手寫 try-catch,除非需測試無異常路徑或進行極特殊邏輯驗證
  5. 測試命名:testMethodName_Condition_ExpectedException 格式提升可讀性

遷移提示:JUnit 5 (Jupiter) 新寫法

// JUnit 5 原生支援異常斷言,無需 Rule
assertThrows(BizException.class, () -> Password.validate(""),
    "應拋出 BizException");
assertThrows(BizException.class, () -> Password.validate(""),
    e -> assertTrue(e.getMessage().contains("required")));

若專案已升級 JUnit 5,建議直接使用 org.junit.jupiter.api.Assertions.assertThrows,API 更直觀且無 Rule 機制負擔。

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