JUnit是由Erich Gamma 和Kent Beck 編寫的一個回歸測試框架(regression testing framework)。 Junit測試是程序員測試,即白盒測試。該項目主頁:http://www.junit.org/
使用JUnit時,主要都是通過繼承TestCase類別來撰寫測試用例,使用testXXX()名稱來撰寫單元測試。
用JUnit寫測試真正所需要的就三件事:
1. 一個import語句引入所有junit.framework.*下的類。
2. 一個extends語句讓你的類從TestCase繼承。
3. 一個調用super(string)的構造函數。
功能類MathTool
package com.zj.c01;public class MathTool { public static int gcd(int num1, int num2) { int r = 0; while (num2 != 0) { r = num1 % num2; num1 = num2; num2 = r; } return num1; } }測試類MathToolTest
package com.zj.c01;import junit.framework.TestCase; public class MathToolTest extends TestCase { public MathToolTest(String name) { super(name); } public void testGcd() { assertEquals(5, MathTool.gcd(10, 5)); } }我們在用JUnit 測試方法異常的時候,最容易想到的辦法就是用try…catch 去捕獲異常,需要斷言以下幾個條件:
1. 確實拋出的異常2. 拋出異常的Class 類型3. 拋出異常的具體類型,一般檢查異常的message 屬性中包含的字符串的斷定所以常用的代碼你可能會這麼寫:
@Test public void testBizException(){ try{ Password.validate( "123" ); fail( "No exception thrown." ); }catch ( Exception ex ) { assertTrue( ex instanceof BizException ); assertTrue( ex.getMessage().contains( "error" ) ); } }這裡被測試的方法是Password.validate() 方法是否拋出了相應的異常,注意這裡別漏try 中的fail(“No Exception thrown.”)
代碼行,不然如果被測試的方法如果沒有拋出異常的話,這個用例是通過的,而你預期的是要拋出異常的。
在JUnit 4 中,大可不必如此這般的去測試方法異常。雖然這樣也能測定出是否執行出預期的異常來,但它仍有弊端,接下來會一對比就知道了,try…catch 的方法,JUnit 無法為你提示出詳細的斷言失敗原因。
那麼來看看自從JUnit 4 後可以怎麼去測試異常呢?用@Test(execpted=Exception.class) 註解就行,參考如下代碼:
@Test( expected = BizException.class ) public void testBizException(){ Password.validate( null ); }
如果被測試的方法有拋出BizException類型便是斷言成功,對了@Test(expected = BizException.class) 只能判斷出異常的類型,並無相應的註解能斷言出異常的更具體的信息,即無法判定拋出異常的message 屬性。
那麼,有時候我們會在一個方法中多次拋出一種類型的異常,但原因不同,即異常的message 信息不同,比如出現BizException 時會有以下兩種異常:
new BizException(“Password must contains at least 6 letters.”) new BizException(“Password length less than 15 letters”)
這就要有辦法去斷言異常的message 了,針對於此,自JUnit 4.7 之後又給了我們更完美的選擇,就是下面的代碼:
@Rule public ExpectedException expectedEx = ExpectedException.none(); @Test public void testBizException() throws InvalidPasswordException{ expectedEx.expect( BizException.class ); expectedEx.expectMessage( "required" ); Password.validate( "" ); }上面代碼需重點關注幾個:
1. @Rule 註解的ExpectedException 變量聲明,它必須為public
2. @Test 處,不能寫成@Test(expected=BizException.class),否則不能正確測試,也就是@Test(expected=BizException.class) 和測試方法中的expectedEx.expectXxx() 方法是不能同時並存的3. expectedEx.expectMessage() 中的參數是Matcher 或subString,就是說可用正則表達式判定,或判斷是否包含某個子字符串4. 再就是有一點很重,把被測試方法寫在expectedEx.expectXxx() 方法後面,不然也不能正確測試的異常5. 最後一個是,只要測試方法直接拋出被測試方法的異常即可,並不影響你所關心的異常前面說到用try…catch 的辦法也能正確測試到異常,@Test(expected=…) 或@Rule 與try…catch 的方法對比有什麼好處呢,顯然用JUnit 4 推薦的方法簡潔明了。再來看測試失敗時JUnit 會為你提示什麼呢?
try…catch 測試異常失敗時,得到的提示:
無異常時:
java.lang.AssertionError: No exception thrown. at org.junit.Assert.fail(Assert.java:91) at cc.unmi.PasswordTest.passwordLengthLessThan6LettersThrowsException(PasswordTest.java:20)
異常類型不對或異常的message 不對時:
java.lang.AssertionError: at org.junit.Assert.fail(Assert.java:91) at org.junit.Assert.assertTrue(Assert.java:43) at org.junit.Assert.assertTrue(Assert.java:54) at cc.unmi.PasswordTest.passwordLengthLessThan6LettersThrowsException(PasswordTest.java:22)
上面能提供給我們的定位錯誤的幫助不是特別大再看@Test(expected=BizException.class) 時測試失敗時的提示:
java.lang.AssertionError: Expected exception: cc.test.BizException at org.junit.internal.runners.statements.ExpectException.evaluate(ExpectException.java:32) at org.junit.rules.ExpectedException$ExpectedExceptionStatement.evaluate(ExpectedException.java:110)
用@Rules ExpectedException方式來測試異常,失敗時的提示:
java.lang.AssertionError: Expected: (exception with message a string containing “YES. required” and an instance of java.lang.NullPointerException) got: at org.junit.Assert.assertThat(Assert.java:778) at org.junit.Assert.assertThat(Assert.java:736) at org.junit.rules.ExpectedException$ExpectedExceptionStatement.evaluate(ExpectedException.java:114)
特別是@Rules ExpectedException 方法時為何測試失敗提示的清清楚楚。期望什麼異常,異常message 中含何字符串,實際上確得到什麼類型的異常,異常中message 是什麼。有了這,你一看到就知道怎麼去修補你的程序。