使用JUnit測試預期異常

開發人員常常使用單元測試來驗證的一段兒代碼的操作,很多時候單元測試可以檢查拋出預期異常( expected exceptions)的代碼。在Java語言中,JUnit是一套標準的單元測試方案,它提供了很多驗證拋出的異常的機制。本文就探討一下他們的優點。

我們拿下面的代碼作爲例子,寫一個測試,確保canVote() 方法返回true或者false, 同時你也能寫一個測試用來驗證這個方法拋出的IllegalArgumentException異常。

public class Student {
  public boolean canVote(int age) {
      if (i<=0) throw new IllegalArgumentException("age should be +ve");
      if (i<18) return false;
      else return true;
  }
}

(Guava類庫中提供了一個作參數檢查的工具類--Preconditions類,也許這種方法能夠更好的檢查這樣的參數,不過這個例子也能夠檢查)。

檢查拋出的異常有三種方式,它們各自都有優缺點:

1.@Test(expected…)

@Test註解有一個可選的參數,"expected"允許你設置一個Throwable的子類。如果你想要驗證上面的canVote()方法拋出預期的異常,我們可以這樣寫:

@Test(expected = IllegalArgumentException.class)
public void canVote_throws_IllegalArgumentException_for_zero_age() {
    Student student = new Student();
    student.canVote(0);
}

簡單明瞭,這個測試有一點誤差,因爲異常會在方法的某個位置被拋出,但不一定在特定的某行。

2.ExpectedException

如果要使用JUnit框架中的ExpectedException類,需要聲明ExpectedException異常。

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

然後你可以使用更加簡單的方式驗證預期的異常。

@Test
public void canVote_throws_IllegalArgumentException_for_zero_age() {
    Student student = new Student();
    thrown.expect(NullPointerException.class);
    student.canVote(0);
}

或者可以設置預期異常的屬性信息。

@Test
public void canVote_throws_IllegalArgumentException_for_zero_age() {
    Student student = new Student();
    thrown.expect(IllegalArgumentException.class);
    thrown.expectMessage("age should be +ve");
    student.canVote(0);
}

除了可以設置異常的屬性信息之外,這種方法還有一個優點,它可以更加精確的找到異常拋出的位置。在上面的例子中,在構造函數中拋出的未預期的(unexpected) IllegalArgumentException 異常將會引起測試失敗,我們希望它在canVote()方法中拋出。

從另一個方面來說,如果不需要聲明就更好了

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

它就像不需要的噪音一樣,如果這樣就很好了

expect(RuntimeException.class)

或者:

expect(RuntimeException.class, “Expected exception message”)

或者至少可以將異常和信息當做參數傳進去

thrown.expect(IllegalArgumentException.class, “age should be +ve”);

3.Try/catch with assert/fail

在JUnit4之前的版本中,使用try/catch語句塊檢查異常

@Test
public void canVote_throws_IllegalArgumentException_for_zero_age() {
    Student student = new Student();
    try {
        student.canVote(0);
    } catch (IllegalArgumentException ex) {
        assertThat(ex.getMessage(), containsString("age should be +ve"));
    }
    fail("expected IllegalArgumentException for non +ve age");
}

儘管這種方式很老了,不過還是非常有效的。主要的缺點就是很容易忘記在catch語句塊之後需要寫fail()方法,如果預期異常沒有拋出就會導致信息的誤報。我曾經就犯過這樣的錯誤。

總之,這三種方法都可以測試預期拋出的異常,各有優缺點。對於我個人而言,我會選擇第二種方法,因爲它可以非常精確、高效的測試異常信息。

翻譯自:Java Code Geeks

發佈了146 篇原創文章 · 獲贊 37 · 訪問量 93萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章