咱們拿下面的代碼做爲例子,寫一個測試,確保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類,也許這種方法可以更好的檢查這樣的參數,不過這個例子也可以檢查)。函數
檢查拋出的異常有三種方式,它們各自都有優缺點:工具
@Test註解有一個可選的參數,"expected"容許你設置一個Throwable的子類。若是你想要驗證上面的canVote()方法拋出預期的異常,咱們能夠這樣寫:單元測試
@Test(expected = IllegalArgumentException.class) public void canVote_throws_IllegalArgumentException_for_zero_age() { Student student = new Student(); student.canVote(0); }
簡單明瞭,這個測試有一點偏差,由於異常會在方法的某個位置被拋出,但不必定在特定的某行。測試
若是要使用JUnit框架中的ExpectedException類,須要聲明ExpectedException異常。.net
@Rule public ExpectedException thrown= ExpectedException.none();
而後你能夠使用更加簡單的方式驗證預期的異常。code
@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()方法中拋出。get
從另外一個方面來講,若是不須要聲明就更好了it
@Rule public ExpectedException thrown= ExpectedException.none();
它就像不須要的噪音同樣,若是這樣就很好了
expect(RuntimeException.class)
或者:
expect(RuntimeException.class, 「Expected exception message」)
或者至少能夠將異常和信息當作參數傳進去
thrown.expect(IllegalArgumentException.class, 「age should be +ve」);
在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()方法,若是預期異常沒有拋出就會致使信息的誤報。我曾經就犯過這樣的錯誤。
總之,這三種方法均可以測試預期拋出的異常,各有優缺點。對於我我的而言,我會選擇第二種方法,由於它能夠很是精確、高效的測試異常信息。