前言:JUnit元數據
@Before:
使用了該元數據的方法在每個測試方法執行之前都要執行一次。
@After:
使用了該元數據的方法在每個測試方法執行之后要執行一次。
注意:@Before和@After標示的方法只能各有一個。這個相當于取代了JUnit以前版本中的setUp和tearDown方法,當然你還可以繼續叫這個名字,不過JUnit不會霸道的要求你這么做了。
@Test(expected=*.class)
在JUnit4.0之前,對錯誤的測試,我們只能通過fail來產生一個錯誤,并在try塊里面assertTrue(true)來測試。現在,通過@Test元數據中的expected屬性。expected屬性的值是一個異常的類型
@Test(timeout=xxx):
該元數據傳入了一個時間(毫秒)給測試方法,
如果測試方法在制定的時間之內沒有運行完,則測試也失敗。
@ignore:
該元數據標記的測試方法在測試中會被忽略。當測試的方法還沒有實現,或者測試的方法已經過時,或者在某種條件下才能測試該方法(比如需要一個數據庫聯接,而在本地測試的時候,數據庫并沒有連接),那么使用該標簽來標示這個方法。同時,你可以為該標簽傳遞一個String的參數,來表明為什么會忽略這個測試方 法。比如:@lgnore(“該方法還沒有實現”),在執行的時候,僅會報告該方法沒有實現,而不會運行測試方法。、
一、包含必要地Package
最主要地一個 Package就是org.junit.*,把它包含進來之后,絕大部分功能就有了。還有一句話也非常地重要“import static org.junit.Assert.*;”,我們在測試的時候使用的一系列assertEquals方法就來自這個包。大家注意一下,這是一個靜態包含 (static),是JDK5中新增添的一個功能。也就是說,assertEquals是Assert類中的一系列的靜態方法
二、測試類的聲明
測試類是一個獨立的類,沒有任何父類。測試類的名字也可以任意命名,沒有任何局限性。它與普通類的區別在于它內部的方法的聲明
三、創建一個待測試的對象
你要測試哪個類,那么你首先就要創建一個該類的對象。
private staticCalculator calculator =newCalculator();
為了測試Calculator類,我們必須創建一個calculator對象。
四、測試方法的聲明
在測試類中,并不是每一個方法都是用于測試的,你必須使用“標注”來明確表明哪些是測試方法。“標注”也是JDK5的一個新特性,用在此處非常恰當。我們可以看到,在某些方法的前有@Before、@Test、@Ignore等字樣,這些就是標注,以一個“@”作為開頭。這些標注都是JUnit4自定義 的,熟練掌握這些標注的含義非常重要。
六、 忽略測試某些尚未完成的方法
七、 Fixture(暫且翻譯為“固定代碼段”)
Fixture 的含義就是“在某些階段必然被調用的代碼”。“在任何一個測試執行之前必須執行的代碼”就是一個Fixture,我們用@Before來標注它
一、 高級Fixture
兩個Fixture標注,分別是@Before和@After,是否適合完成如下功能:有一個類是負責對大文件(超過 500兆)進行讀寫,他的每一個方法都是對文件進行操作。換句話說,在調用每一個方法之前,我們都要打開一個大文件并讀入文件內容,這絕對是一個非常耗費時間的操作。如果我們使用@Before和@After,那么每次測試都要讀取一次文件,效率及其低下。這里我們所希望的是在所有測試一開始讀一次文件, 所有測試結束之后釋放文件,而不是每次測試都讀文件。JUnit的作者顯然也考慮到了這個問題,它給出了@BeforeClass 和 @AfterClass兩個Fixture來幫我們實現這個功能。從名字上就可以看出,用這兩個Fixture標注的函數,只在測試用例初始化時執行@BeforeClass方法,當所有測試執行完畢之后,執行@AfterClass進行收尾工作。在這里要注意一下,每個測試類只能有一個方法被標注為 @BeforeClass或@AfterClass,并且該方法必須是Public和Static的。
二、 限時測試
那個求平方根的函數有Bug,是個死循環:
public voidsquareRoot(intn) ...{
for(; ;) ;//Bug : 死循環
}
如果測試的時候遇到死循環,對于那些邏輯很復雜,循環嵌套比較深的程序,很有可能出現死循環,因此一定要采取一些預防措施。我們給這些測試函數設定一個執行時間,超過了這個時間,他們就會被系統強行終止,并且系統還會向你匯報該函數結束的原因是因為超時,這樣你就可以發現這些Bug了。只需要給@Test標注加一個參數即可,代碼如下:
@Test(timeout = 1000)
public voidsquareRoot() ...{
calculator.squareRoot(4);
assertEquals(2,calculator.getResult());
}
Timeout參數表明了你要設定的時間,單位為毫秒,因此1000就代表1秒。
三、 測試異常
經常會編寫一些需要拋出異常的函數,如果一個函數應該拋出異常,但是它沒拋出,當然是Bug。例如,我們寫的計算器類有除法功能,如果除數是一個0,那么必然要拋出“除0異常”。因此,我們很有必要對這些進行測試。代碼如下:
@Test(expected = ArithmeticException.class)
public void divideByZero() ...{
calculator.divide(0);
}
如上述代碼所示,我們需要使用@Test標注的expected屬性,將我們要檢驗的異常傳遞給他,這樣JUnit框架就能自動幫我們檢測是否拋出了我們指定的異常。
四、 Runner (運行器)
把測試代碼提交給JUnit框架后,框架如何來運行代碼呢?答案就是——Runner。在JUnit中有很多個 Runner,他們負責調用測試代碼,每一個Runner都有各自的特殊功能,要根據需要選擇不同的Runner來運行測試代碼。JUnit中有一個默認Runner,如果沒有指定,那么系統自動使用默認 Runner來運行你的代碼。換句話說,下面兩段代碼含義是完全一樣的:
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
@RunWith(Suite.class)
@Suite.SuiteClasses({
MyTestCase.class, //測試類
PartSuite.class, //另一個測試套
})
public class AllTestCases {
}
要想指定一個Runner,需要使用@RunWith標注,并且把你所指定的Runner作為參數傳遞給它。另外一個要注意的 是,@RunWith是用來修飾類的,而不是用來修飾函數的。只要對一個類指定了Runner,那么這個類中的所有函數都被這個Runner來調用。
五、 參數化測試
一個對考試分數進行評價的函數,返回值分別為“優秀,良好,一般,及格,不及格”,因此你在編寫測試的時候,至少要寫5個測試,把這5中情況都包含了,這 確實是一件很麻煩的事情。我們還使用我們先前的例子,測試一下“計算一個數的平方”這個函數,暫且分三類:正數、0、負數。測試代碼如下:
importorg.junit.AfterClass;
importorg.junit.Before;
importorg.junit.BeforeClass;
importorg.junit.Test;
importstatic org.junit.Assert.*;
public classAdvancedTest ...{
private static Calculator calculator =new Calculator();
@Before
public void clearCalculator() ...{
calculator.clear();
}
@Test
public void square1() ...{
calculator.square(2);
assertEquals(4,calculator.getResult());
}
@Test
public void square2() ...{
calculator.square(0);
assertEquals(0, calculator.getResult());
}
@Test
public void square3() ...{
calculator.square(-3);
assertEquals(9,calculator.getResult());
}
}
為了簡化類似的測試,JUnit4提出了“參數化測試”的概念,只寫一個測試函數,把這若干種情況作為參數傳遞進去,一次性的完成測試。代碼如下:
importstatic org.junit.Assert.assertEquals;
importorg.junit.Test;
importorg.junit.runner.RunWith;
importorg.junit.runners.Parameterized;
importorg.junit.runners.Parameterized.Parameters;
importjava.util.Arrays;
importjava.util.Collection;
@RunWith(Parameterized.class)
public classSquareTest{
private static Calculator calculator = new Calculator();
private int param;
private int result;
@Parameters
public static Collection data(){
return Arrays.asList(newObject[][]...{
{2, 4},
{0, 0},
{-3, 9},
});
}
//構造函數,對變量進行初始化
六、斷言和假設
斷言:org.junit.Assert用于測試用例中,如果斷言失敗,用例即結束。
假設:org.junit.Assume用于在準備環境時判斷環境是否符合要求,包括測試套的@BeforeClass,測試類的@BeforeClass,測試類的實例化,測試類的@Before。
如果假設失敗,假設所處初始化代碼方法立即結束,更深級別的后續工作也被忽略,相關測試用例被忽略,但與假設同級別的收尾工作還要繼續執行。
例如:如果在測試類的@BeforeClass中假設失敗,該類的實例化及子級別將被忽略,@AfterClass會繼續執行。
七、工程實例
如果不想在單元測試中操作數據庫中的數據。可以在測試方法上加
@Test
@Transactional //單元測試 @Transactional 不會進行數據提交事物
@Rollback(true) // 這個注釋可以不用加(單元測試默認值)
public void testaddUserPrivate()throws Exception{
UserPrivate userPrivate=new UserPrivate();
userPrivate.setTenantId("31");
userPrivate.setMenuCode("7777888");
userPrivate.setpUid("111222");
userPrivate.setRoleId("3332277");
userPrivate.setValue("99999");
userPrivate.setUpdateDateTime(new Date());
userPrivate.setCreateDateTime(new Date());
int s=userPrivateService.addUserPrivate(userPrivate);
Assert.assertEquals(1, s); // 這是斷言的使用
}