首先咱們須要先下載相應的 JUnit 相關的 JAR 包,下載的過程能夠去 JUnit 的官方網站,也能夠直接經過 Maven 資源倉庫來完成,我這裏直接經過開源中國社區在國內的Maven 鏡像下載了 JUnit-4.8.2.jar 的版本,以下圖所示: html
直接搜索關鍵字"junit"便可,咱們能夠從搜索結果中找到紅色方框1中的對應項,選中以後就會在左下方列出目前能夠得到的 junit 的全部版本,這裏我選擇了 4.8.2 的版本(紅色方框2),而後點擊右側的 Download 按鈕便可下載這個 JAR 包,不過依我在實踐中的經驗,最好同時下載對應版本的 javadoc 和 source 兩個包,前者是文檔,後者是對應的源代碼,而後將它們直接引入到咱們的 eclipse 的工程中便可。我這裏建立了壹個普通的 Java Project ,並給它取名 junit-study ,導入 JAR 包以後的樣子以下圖所示。到此,咱們使用 JUnit 的準備工做已經作完。 java
準備工做作好以後,接下來咱們就能夠開始嘗試編寫壹個簡單的測試代碼了。首先,咱們編寫了壹個 Calculator 類,並提供五個方法分別完成加減乘除以及求平方的運算。代碼以下: 數據庫
package net.oschina.bairrfhoinn.main; public class Calculator { public void add(int n){ result += n; } public void substract(int n){ result -= n; } public void multiply(int n){ result *= n; } public void divide(int n){ result /= n; } public void square(int n){ result = n * n; } public int getReuslt(){ return result; } public void clear(){ result = 0; } private static int result; }在測試類中用到了JUnit4框架,天然要把相應地Package包含進來。最主要地一個Package就是org.junit.*。把它包含進來以後, 絕大部分功能就有了。還有一句話也很是地重要「import static org.junit.Assert.*;」,咱們在測試的時候使用的壹系列assertEquals()方法就來自這個包。你們注意壹下,這是壹個靜態包 含(static),是JDK5中新增添的壹個功能。也就是說,assertEquals是Assert類中的壹系列的靜態方法,壹般的使用方式是 Assert. assertEquals(),可是使用了靜態包含後,前面的類名就能夠省略了,使用起來更加的方便。
package net.oschina.bairrfhoinn.test; import static org.junit.Assert.*; import org.junit.Test; import net.oschina.bairrfhoinn.main.Calculator; public class CalculatorTest { private static Calculator calculator = new Calculator(); @Test public void testAdd(){ calculator.add(7); calculator.add(8); assertEquals(15, calculator.getReuslt()); } }首先,咱們要在方法的前面使用@Test標註,以代表這是壹個測試方法。對於方法的聲明也有以下要求:名字能夠隨便取,沒有任何限制,可是返回值必須爲void,並且不能有任何參數。若是違反這些規定,會在運行時拋出壹個異常。至於方法內該寫些什麼,那就要看你須要測試些什麼了。好比上述代碼中,咱們想測試壹下add()方法的功能是否正確,就在測試方法中調用幾回add函數,初始值爲0,先加7,再加8,咱們期待的結果應該是15。若是最終實際結果也是15,則說明add()方法是正確的,反之說明它是錯的。assertEquals(15, calculator.getResult());就是用來判斷期待結果和實際結果是否相等,其中第壹個參數填寫期待結果,第二個參數填寫實際結果,也就是經過計算獲得的結果。這樣寫好以後,JUnit 會自動進行測試並把測試結果反饋給用戶。
若是想運行它,能夠在 eclipse 的資源管理器中選擇該類文件,而後點擊右鍵,選擇 Run As->JUnit Test 便可看到運行結果以下圖所示: 數組
若是你在寫程序前作了很好的規劃,那麼哪些方法是什麼功能都應該實現而且肯定下來。所以,即便該方法還沒有完成,他的具體功能也是肯定的,這也就意味着你能夠爲他編寫測試用例。可是,若是你已經把該方法的測試用例寫完,但該方法還沒有完成,那麼測試的時候無疑是「失敗」。這種失敗和真正的失敗是有區別的,所以 JUnit 提供了壹種方法來區別他們,那就是在這種測試函數的前面加上 @Ignore 標註,這個標註的含義就是「某些方法還沒有完成,暫不參與這次測試」。這樣的話測試結果就會提示你有幾個測試被忽略,而不是失敗。壹旦你完成了相應函數,只須要把@Ignore標註刪去,就能夠進行正常的測試。
好比說上面的測試類 Calculator.java 中,假設咱們的 Calculator 類的 multiply() 方法沒有實現,咱們能夠在測試類 CalculatorTest 中先寫以下測試代碼: 框架
package net.oschina.bairrfhoinn.test; import static org.junit.Assert.*; import org.junit.Ignore; import org.junit.Test; import net.oschina.bairrfhoinn.main.Calculator; public class CalculatorTest { private static Calculator calculator = new Calculator(); ... //此處代碼省略 @Ignore("method square() not implemented, please test this later...") @Test public void testSquare(){ calculator.square(3); assertEquals(9, calculator.getReuslt()); } }
咱們再運行壹次測試,會看到以下結果,從圖中能夠很明顯的看出,方法testSquare() 上的 @Ignore 註解已經生效了,運行時直接跳過了它,而方法testAdd()仍然正常的運行並經過了測試。 eclipse
前置工做一般是指咱們的測試方法在運行以前須要作的壹些準備工做,如數據庫的鏈接、文件的加載、輸入數據的準備等須要在運行測試方法以前作的事情,都屬於 前置工做;相似的,後置工做則是指測試方法在運行以後的壹些要作的事情,如釋放數據庫鏈接、輸入輸出流的關閉等;好比咱們上面的測試,因爲只聲明瞭壹個 Calculator 對象,他的初始值是0,可是測試完加法操做後,他的值就不是0了;接下來測試減法操做,就必然要考慮上次加法操做的結果。這絕對是壹個很糟糕的設計!咱們 很是但願每壹個測試方法都是獨立的,相互之間沒有任何耦合度。所以,咱們就頗有必要在執行每壹個測試方法以前,對Calculator對象進行壹個「復 原」操做,以消除其餘測試形成的影響。所以,「在任何壹個測試方法執行以前必須執行的代碼」就是壹個前置工做,咱們用註解 @Before 來標註它,以下例子所示: maven
package net.oschina.bairrfhoinn.test; ... import org.junit.After; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; public class CalculatorTest { ...//這裏省略部分代碼 @Before public void setUp() throws Exception { calculator.clear(); } @After public void tearDown() throws Exception { System.out.println("will do sth here..."); } ...//這裏省略部分代碼 }
另外要說的是,註解 @Before 是定義在 org.junit.Before 這個類中的,所以使用時須要將其引入咱們的代碼中。這樣作了以後,每次咱們運行測試方法時,JUnit 都會先運行 setUp() 方法將 result 的值清零。不過要注意的是,這裏再也不須要 @Test 註解,由於這並非壹個 test,只是壹個前置工做。同理,若是「在任何測試執行以後須要進行的收尾工做,咱們應該使用 @After 來標註,方法與它相似。因爲本例比較簡單,不須要用到此功能,因此咱們只是簡單了給它添加了壹個 tearDown() 方法並在收尾時打印壹句話到控制檯,而且使用 @After 來註解這個方法。 ide
上面咱們提到了兩個註解 @Before 和 @After ,咱們來看看他們是否適合完成以下功能:有壹個類負責對大文件(超過500 MB)進行讀寫,他的每壹個方法都是對文件進行操做。換句話說,在調用每壹個方法以前,咱們都要打開壹個大文件並讀入文件內容,這絕對是壹個很是耗費時的 操做。若是咱們使用 @Before 和 @After ,那麼每次測試都要讀取壹次文件,效率及其低下。因此咱們但願的是,在全部測試壹開始讀壹次文件,全部測試結束以後釋放文件,而不是每次測試都讀文件。 JUnit的做者顯然也考慮到了這個問題,它給出了@BeforeClass 和 @AfterClass 兩個註解來幫咱們實現這個功能。從名字上就能夠看出,用這兩個註解標註的函數,只在測試用例初始化時執行 @BeforeClass 方法,當全部測試執行完畢以後,執行 @AfterClass 進行收尾工做。在這裏要注意壹下,每個測試類只能有壹個方法被標註爲 @BeforeClass 或 @AfterClass,並且該方法必須是 public static 類型的。 函數
如今假設咱們的 Calculator 類中的 square() 方法是個死循環,那應該怎麼辦呢,好比說像下面這樣: 測試
public void square(int n){ for(;;){} }
若是測試的時候遇到死循環,你的臉上絕對不會露出笑容的。所以,對於那些邏輯很複雜,循環嵌套比較深的、有可能出現死循環的程序,所以壹定要採起壹些預防 措施。限時測試是壹個很好的解決方案。咱們給這些測試函數設定壹個預期的執行時間,超過了這壹時間,他們就會被系統強行終止,而且系統還會向你彙報該函數 結束的緣由是由於超時,這樣你就能夠發現這些 Bug 了。要實現這壹功能,只須要給 @Test 標註加壹個參數timeout便可,代碼以下:
@Test(timeout=2000L) public void testSquare() { calculator.square(3); assertEquals(9, calculator.getReuslt()); }timeout參數代表了你預計該方法運行的時長,單位爲毫秒,所以2000就表明2秒。如今咱們讓這個測試方法運行壹下,看看失敗時是什麼效果:
JAVA中的異常處理也是壹個重點,所以你常常會編寫壹些須要拋出異常的函數。若是你以爲壹個函數應該拋出異常,可是它沒拋出,這算不算 Bug 呢?這固然是Bug,JUnit 也考慮到了這壹點,而且能夠幫助咱們找到這種 Bug。例如,咱們寫的計算器類有除法功能,若是除數是壹個0,那麼必然要拋出「除0異常」。所以,咱們頗有必要對這些進行測試。代碼以下:
@Test(expected=java.lang.ArithmeticException.class) public void testDivide(){ calculator.divide(0); }如上述代碼所示,咱們須要使用@Test註解中的expected屬性,將咱們要檢驗的異常(這裏是 java.lang.ArithmeticException)傳遞給他,這樣 JUnit 框架就能自動幫咱們檢測是否拋出了咱們指定的異常。
你們有沒有想過這個問題,當你把測試代碼提交給JUnit框架後,框架是如何來運行你的代碼的呢?答案就是Runner。在JUnit中有不少個 Runner,他們負責調用你的測試代碼,每壹個Runner都有其各自的特殊功能,你要根據須要選擇不一樣的Runner來運行你的測試代碼。可能你會覺 得奇怪,前面咱們寫了那麼多測試,並無明確指定壹個Runner啊?這是由於JUnit中有壹個默認的Runner,若是你沒有指定,那麼系統會自動使 用默認Runner來運行你的代碼。換句話說,下面兩段代碼含義是徹底壹樣的:
import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @RunWith(JUnit4.class) public class CalculatorTest { ...//省略此處代碼 } //用了系統默認的JUnit4.class,運行效果徹底壹樣 public class CalculatorTest { ...//省略此處代碼 }從上述例子能夠看出:
你可能遇到過這樣的函數,它的參數有許多特殊值,或者說他的參數分爲不少個區域。好比,壹個對考試分數進行評價的函數,返回值分別爲「優秀,良好,壹般, 及格,不及格」,所以你在編寫測試的時候,至少要寫5個測試,把這五種狀況都包含了,這確實是壹件很麻煩的事情。這裏咱們仍然使用先前的例子,測試壹下 square()這個函數,暫且分三類:正數、0、負數。測試代碼以下:
package net.oschina.bairrfhoinn.test; import static org.junit.Assert.*; import net.oschina.bairrfhoinn.main.Calculator; import org.junit.Before; import org.junit.Test; public class AdvancedTest { private static Calculator calculator = new Calculator(); @Before public void setUp() throws Exception { calculator.clear(); } @Test public void testSquare1(){ calculator.square(2); assertEquals(4, calculator.getReuslt()); } @Test public void testSquare2(){ calculator.square(0); assertEquals(0, calculator.getReuslt()); } @Test public void testSquare3(){ calculator.square(-3); assertEquals(9, calculator.getReuslt()); } }爲了簡化相似的測試,JUnit4提出了「參數化測試」的概念,只寫壹個測試函數,把這若干種狀況的輸入參數和預期的運行結果放在集合中,而後將這個集合做爲參數傳遞進去,壹次性的完成測試。代碼以下:
package net.oschina.bairrfhoinn.test; import static org.junit.Assert.*; import java.util.Arrays; import java.util.Collection; import net.oschina.bairrfhoinn.main.Calculator; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; @RunWith(Parameterized.class) public class SquareTest { @Parameters public static Collection prepareData(){ return Arrays.asList(new Object[][]{{2,4},{0, 0},{-3, 9}}); } public SquareTest(int param, int result){ this.param = param; this.result = result; } @Test public void square(){ calculator.square(param); assertEquals(result, calculator.getReuslt()); } private int param; private int result; private static Calculator calculator = new Calculator(); }下面咱們對上述代碼進行分析。首先,你要爲這種測試專門生成壹個新的類,而不能與其餘測試共用同壹個類,此例中咱們定義了壹個 SquareTest 類。而後,你要爲這個類指定壹個 Runner,而不能使用默認的 Runner 了,由於特殊的功能要用特殊的Runner 了。@RunWith(Parameterized.class) 這條語句就是爲這個類指定了壹個 Parameterized 的 Runner。第二步,定義壹個待測試的類,而且定義兩個變量,壹個用於存放參數,另外壹個用於存放期待的結果。接下來,定義測試數據的集合,也就是上述 的prepareData() 方法,該方法實際能夠任意命名,可是必須使用 @Parameters 註解進行修飾。這個方法的框架就不予解釋了,你們只須要注意其中的數據,是壹個二維數組,數據兩兩壹組,每組中的這兩個數據,壹個是參數,另外壹個是你預 期的結果。好比咱們的第壹組{2, 4},2 就是參數,4 就是預期的結果。這兩個數據的順序無所謂,誰前誰後均可以。以後是構造函數,其功能就是對先前定義的兩個參數進行初始化。在這裏你可要注意一下參數的順序 了,要和上面的數據集合的順序保持壹致。若是前面的順序是{參數,期待的結果},那麼你構造函數的順序也要是「構造函數(參數, 期待的結果)」,反之亦然。最後就是寫壹個簡單的測試用例了,和前面介紹過的寫法徹底壹樣,在此就很少說。
經過前面的介紹咱們能夠感受到,在壹個項目中,只寫壹個測試類是不可能的,咱們會寫出不少不少個測試類。但是這些測試類必須壹個壹個的執行,也是比較麻煩 的事情。鑑於此,JUnit 爲咱們提供了打包測試的功能,將全部須要運行的測試類集中起來,壹次性的運行完畢,大大的方便了咱們的測試工做。具體代碼以下:
package net.oschina.bairrfhoinn.test; import org.junit.runner.RunWith; import org.junit.runners.Suite; @RunWith(Suite.class) @Suite.SuiteClasses({CalculatorTest.class, SquareTest.class, AdvancedTest.class}) public class AllCalculatorTests { //這裏什麼都不作 }
咱們能夠看到,這個功能也須要使用壹個特殊的Runner,所以咱們須要向@RunWith 註解傳遞壹個參數Suite.class。同時,咱們還須要另外壹個註解@Suite.SuiteClasses,來代表這個類是壹個打包測試類。咱們把 須要打包的類做爲參數傳遞給該標註就能夠了。有了這兩個註解以後,就已經完整的表達了全部的含義,所以下面的類已經可有可無,隨便起壹個類名,內容所有爲 空既可。想對全部測試類進行測試時,只須要運行這壹個測試類便可。運行以後的結果以下圖所示。