[轉]在Eclipse中使用JUnit4進行單元測試(高級篇)

經過前2篇文章,您必定對JUnit有了一個基本的瞭解,下面咱們來探討一下JUnit4中一些高級特性。java

 

1、高級Fixture數組

上一篇文章中咱們介紹了兩個Fixture標註,分別是@Before和@After,咱們來看看他們是否適合完成以下 功能:有一個類是負責對大文件(超過500兆)進行讀寫,他的每個方法都是對文件進行操做。換句話說,在調用每個方法以前,咱們都要打開一個大文件並 讀入文件內容,這絕對是一個很是耗費時間的操做。若是咱們使用@Before和@After,那麼每次測試都要讀取一次文件,效率及其低下。這裏咱們所希 望的是在全部測試一開始讀一次文件,全部測試結束以後釋放文件,而不是每次測試都讀文件。JUnit的做者顯然也考慮到了這個問題,它給出了 @BeforeClass 和 @AfterClass兩個Fixture來幫咱們實現這個功能。從名字上就能夠看出,用這兩個Fixture標註的函數,只在測試用例初始化時執行 @BeforeClass方法,當全部測試執行完畢以後,執行@AfterClass進行收尾工做。在這裏要注意一下,每一個測試類只能有一個方法被標註爲 @BeforeClass 或 @AfterClass,而且該方法必須是Public和Static的框架

 

限時測試ide

還記得我在初級篇中給出的例子嗎,那個求平方根的函數有Bug,是個死循環:函數

public void squareRoot(int n)
    {
        for(;;);    //Bug:死循環
    }

若是測試的時候遇到死循環,你的臉上絕對不會露出笑容。所以,對於那些邏輯很複雜,循環嵌套比較深的程序,頗有可能出現 死循環,所以必定要採起一些預防措施。限時測試是一個很好的解決方案。咱們給這些測試函數設定一個執行時間,超過了這個時間,他們就會被系統強行終止,並 且系統還會向你彙報該函數結束的緣由是由於超時,這樣你就能夠發現這些Bug了。要實現這一功能,只須要給@Test標註加一個參數便可,代碼以下:測試

    @Test(timeout=1000)
    public void testAdd() {
        calculator.squareRoot(4);
        assertEquals(2, calculator.getResult());
    }
運行結果:

Timeout參數代表了你要設定的時間,單位爲毫秒,所以1000就表明1秒。ui

 

3、測試異常this

JAVA中的異常處理也是一個重點,所以你常常會編寫一些須要拋出異常的函數。那麼,若是你以爲一個函數應該拋出異常, 可是它沒拋出,這算不算Bug呢?這固然是Bug,並JUnit也考慮到了這一點,來幫助咱們找到這種Bug。例如,咱們寫的計算器類有除法功能,若是除 數是一個0,那麼必然要拋出「除0異常」。所以,咱們頗有必要對這些進行測試。代碼以下:spa

   @Test(expected =ArithmeticException.class)
    public void testDivide() {
        calculator.add(8);
        calculator.divide(0);
        assertEquals(4,calculator.getResult());
    }

如上述代碼所示,咱們須要使用@Test標註的expected屬性,將咱們要檢驗的異常傳遞給他,這樣JUnit框架就能自動幫咱們檢測是否拋出了咱們指定的異常。3d

 

4、Runner (運行器)

你們有沒有想過這個問題,當你把測試代碼提交給JUnit框架後,框架如何來運行你的代碼呢?答案就是——Runner。在JUnit中有不少個Runner,他們負責調用你的測試代碼,每個Runner都有各自的特殊功能,你要根據須要選擇不一樣的Runner來運行你的測試代碼。可能你會以爲奇怪,前面咱們寫了那麼多測試,並無明確指定一個Runner啊?這是由於JUnit中有一個默認Runner,若是你沒有指定,那麼系統自動使用默認Runner來運行你的代碼。換句話說,下面兩段代碼含義是徹底同樣的:

import org.junit.internal.runners.TestClassRunner;
import org.junit.runner.RunWith;

@RunWith(TestClassRunner.class)
public class CalculatorTest {
...
}

//使用了系統默認的TestClassRunner,與下面代碼徹底同樣
public class CalculatorTest {
...
}

從上述例子能夠看出,要想指定一個Runner,須要使用@RunWith標註,而且把你所指定的Runner做爲參數傳遞給它。另一個要注意的是,@RunWith是用來修飾類的,而不是用來修飾函數的。只要對一個類指定了Runner,那麼這個類中的全部函數都被這個Runner來調用。最後,不要忘了包含相應的Package哦,上面的例子對這一點寫的很清楚了。接下來,我會向大家展現其餘Runner的特有功能。

 

5、參數化測試

你可能遇到過這樣的函數,它的參數有許多特殊值,或者說他的參數分爲不少個區域。好比,一個對考試分數進行評價的函數, 返回值分別爲「優秀,良好,通常,及格,不及格」,所以你在編寫測試的時候,至少要寫5個測試,把這5中狀況都包含了,這確實是一件很麻煩的事情。咱們還 使用咱們先前的例子,測試一下「計算一個數的平方」這個函數,暫且分三類:正數、0、負數。測試代碼以下:

import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.*;

 
public class AdvancedTest {
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提出了「參數化測試」的概念,只寫一個測試函數,把這若干種狀況做爲參數傳遞進去,一次性的完成測試。代碼以下:

import static org.junit.Assert.*;
import java.util.Collection;
import java.util.Arrays;
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 {
    private static Calculator calculator = new Calculator();

    private int param;
    private int result;
    
    @Parameters
    public static Collection data() {
        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.getResult());
        System.out.println("result="+result);
    }
}

下面咱們對上述代碼進行分析。首先,你要爲這種測試專門生成一個新的類,而不能與其餘測試共用同一個類,此例中咱們定義了一個SquareTest類。而後,你要爲這個類指定一個Runner,而不能使用默認的Runner了,由於特殊的功能要用特殊的Runner嘛。

@RunWith(Parameterized.class)這條語句就是爲這個類指定了一個ParameterizedRunner。

第二步,定義一個待測試的類,而且定義兩個變量,一個用於存放參數,一個用於存放期待的結果。

接下來,定義測試數據的集合,也就是上述的data()方法,該方法能夠任意命名,但是必須使用@Parameters標註進行修飾。這個方法的框架就不予解釋了,你們只須要注意其中的數據,是一個二維數組,數據兩兩一組,每組中的這兩個數據,一個是參數,一個是你預期的結果。好比咱們的第一組{2, 4},2就是參數,4就是預期的結果。這兩個數據的順序無所謂,誰前誰後均可以。

以後是構造函數,其功能就是對先前定義的兩個參數進行初始化。在這裏你可要注意一下參數的順序了,要和上面的數據集合的 順序保持一致。若是前面的順序是{參數,期待的結果},那麼你構造函數的順序也要是「構造函數(參數, 期待的結果)」,反之亦然。最後就是寫一個簡單的測試例了,和前面介紹過的寫法徹底同樣,在此就很少說。

 

6、打包測試

經過前面的介紹咱們能夠感受到,在一個項目中,只寫一個測試類是不可能的,咱們會寫出不少不少個測試類。但是這些測試類 必須一個一個的執行,也是比較麻煩的事情。鑑於此,JUnit爲咱們提供了打包測試的功能,將全部須要運行的測試類集中起來,一次性的運行完畢,大大的方 便了咱們的測試工做。具體代碼以下:

import org.junit.Assert.*;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;

@RunWith(Suite.class)
@Suite.SuiteClasses({
        CalculatorTest.class,
        SquareTest.class
        })
public class AllCalculatorTests {
}

運行結果:

你們能夠看到,這個功能也須要使用一個特殊的Runner,所以咱們須要向@RunWith標註傳遞一個參數 Suite.class。同時,咱們還須要另一個標註@Suite.SuiteClasses,來代表這個類是一個打包測試類。咱們把須要打包的類做爲 參數傳遞給該標註就能夠了。有了這兩個標註以後,就已經完整的表達了全部的含義,所以下面的類已經可有可無,隨便起一個類名,內容所有爲空既可。

 

JUnit提供了一種批量運行測試類的方法,叫測試套件。

  測試套件的寫法須要遵循如下原則:

  1. 建立一個空類做爲測試套件的入口;

  2. 使用註解 org.junit.runner.RunWith 和 org.junit.runners.Suite.SuitClasses 修飾這個空類

  3. 將org.junit.runners.Suite做爲參數傳入給註解RunWith,以提示Junit爲此類測試使用套件運行器執行。

  4. 將須要放入此測試套件的測試類組成數組做爲註解SuiteClasses的參數

  5. 保證這個空類使用public修飾,並且存在公開的不帶任何參數的構造函數.

 

 

至此,本系列文章所有結束,但願可以對你們使用JUnit4有所幫助。

相關文章
相關標籤/搜索