1. 程式人生 > >在Eclipse中使用JUnit4進行單元測試(高階篇)

在Eclipse中使用JUnit4進行單元測試(高階篇)

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

一、高階Fixture

上一篇文章中我們介紹了兩個Fixture標註,分別是@Before@After,我們來看看他們是否適合完成如下功能:有一個類是負責對大檔案(超過500兆)進行讀寫,他的每一個方法都是對檔案進行操作。換句話說,在呼叫每一個方法之前,我們都要開啟一個大檔案並讀入檔案內容,這絕對是一個非常耗費時間的操作。如果我們使用@Before@After,那麼每次測試都要讀取一次檔案,效率及其低下。這裡我們所希望的是在所有測試一開始讀一次檔案,所有測試結束之後釋放檔案,而不是每次測試都讀檔案。

JUnit的作者顯然也考慮到了這個問題,它給出了@BeforeClass @AfterClass兩個Fixture來幫我們實現這個功能。從名字上就可以看出,用這兩個Fixture標註的函式,只在測試用例初始化時執行@BeforeClass方法,當所有測試執行完畢之後,執行@AfterClass進行收尾工作。在這裡要注意一下,每個測試類只能有一個方法被標註為@BeforeClass @AfterClass,並且該方法必須是PublicStatic的。

二、限時測試。

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

publicvoid squareRoot(
int n) {

        
for (; ;) ;                 //Bug : 死迴圈

    }

如 果測試的時候遇到死迴圈,你的臉上絕對不會露出笑容。因此,對於那些邏輯很複雜,迴圈巢狀比較深的程式,很有可能出現死迴圈,因此一定要採取一些預防措 施。限時測試是一個很好的解決方案。我們給這些測試函式設定一個執行時間,超過了這個時間,他們就會被系統強行終止,並且系統還會向你彙報該函式結束的原 因是因為超時,這樣你就可以發現這些Bug了。要實現這一功能,只需要給@Test標註加一個引數即可,程式碼如下:

    @Test(timeout =1000)

    
public
void squareRoot() {

        calculator.squareRoot(
4);

        assertEquals(
2, calculator.getResult());

 

    }


Timeout引數表明了你要設定的時間,單位為毫秒,因此1000就代表1秒。

三、測試異常

JAVA中的異常處理也是一個重點,因此你經常會編寫一些需要丟擲異常的函式。那麼,如果你覺得一個函式應該丟擲異常,但是它沒丟擲,這算不算Bug呢?這當然是Bug,並JUnit也考慮到了這一點,來幫助我們找到這種Bug。例如,我們寫的計算器類有除法功能,如果除數是一個0,那麼必然要丟擲“除0異常”。因此,我們很有必要對這些進行測試。程式碼如下:

  @Test(expected = ArithmeticException.class)

  
publicvoid divideByZero() {

calculator.divide(
0); 

  }


異常傳遞給他,這樣JUnit框架就能自動幫我們檢測是否丟擲了我們指定的異常。

四、Runner (執行器)

大家有沒有想過這個問題,當你把測試程式碼提交給JUnit框架後,框架如何來執行你的程式碼呢?答案就是——Runner。在JUnit中有很多個Runner,他們負責呼叫你的測試程式碼,每一個Runner都有各自的特殊功能,你要根據需要選擇不同的Runner來執行你的測試程式碼。可能你會覺得奇怪,前面我們寫了那麼多測試,並沒有明確指定一個Runner啊?這是因為JUnit中有一個預設Runner,如果你沒有指定,那麼系統自動使用預設Runner來執行你的程式碼。換句話說,下面兩段程式碼含義是完全一樣的:

import org.junit.internal.runners.TestClassRunner;

import org.junit.runner.RunWith;

 

//使用了系統預設的TestClassRunner,與下面程式碼完全一樣

publicclass CalculatorTest {

...

}


 

 

@RunWith(TestClassRunner.
class)

publicclass CalculatorTest {

...

}


從上述例子可以看出,要想指定一個Runner,需要使用@RunWith標註,並且把你所指定的Runner作為引數傳遞給它。另外一個要注意的是,@RunWith是用來修飾類的,而不是用來修飾函式的。只要對一個類指定了Runner,那麼這個類中的所有函式都被這個Runner來呼叫。最後,不要忘了包含相應的Package哦,上面的例子對這一點寫的很清楚了。接下來,我會向你們展示其他Runner的特有功能。

五、引數化測試。

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

import org.junit.AfterClass;

import org.junit.Before;

import org.junit.BeforeClass;

import org.junit.Test;

importstatic org.junit.Assert.*;

 

publicclass AdvancedTest {

 

privatestatic Calculator calculator = new Calculator();

 

    @Before

publicvoid clearCalculator() {

        calculator.clear();

    }


 

    @Test

    
publicvoid square1() {

        calculator.square(
2);

        assertEquals(
4, calculator.getResult());

    }


 

    @Test

    
publicvoid square2() {

        calculator.square(
0);

        assertEquals(
0, calculator.getResult());

    }


 

    @Test

    
publicvoid square3() {

        calculator.square(
-3);

        assertEquals(
9, calculator.getResult());

    }


 

}


為了簡化類似的測試,JUnit4提出了“引數化測試”的概念,只寫一個測試函式,把這若干種情況作為引數傳遞進去,一次性的完成測試。程式碼如下:

importstatic org.junit.Assert.assertEquals;

import org.junit.Test;

import org.junit.runner.RunWith;

import org.junit.runners.Parameterized;

import org.junit.runners.Parameterized.Parameters;

 

import java.util.Arrays;

import java.util.Collection;

 

@RunWith(Parameterized.
class)

publicclass SquareTest {

 

    
privatestatic Calculator calculator =new Calculator();

    
privateint param;

    
privateint result;

 

    @Parameters

    
publicstatic Collection data() {

        
return Arrays.asList(new Object[][]{

                
{24},

                
{00},

                
{-39},

        }
);

    }


 

 

//建構函式,對變數進行初始化

    
public SquareTest(int param, int result) {

        
this.param = param;

        
this.result = result;

    }


 

    @Test

    
publicvoid square() {

        calculator.square(param);

        assertEquals(result, calculator.getResult());

    }


 

}


下面我們對上述程式碼進行分析。首先,你要為這種測試專門生成一個新的類,而不能與其他測試共用同一個類,此例中我們定義了一個SquareTest類。然後,你要為這個類指定一個Runner,而不能使用預設的Runner了,因為特殊的功能要用特殊的