我們?cè)诰帉?xiě)大型程序的時(shí)候,需要寫(xiě)成千上萬(wàn)個(gè)方法或函數(shù),這些函數(shù)的功能可能很強(qiáng)大,但我們?cè)诔绦蛑兄挥玫皆摵瘮?shù)的一小部分功能,并且經(jīng)過(guò)調(diào)試可以確定,這 一小部分功能是正確的。但是,我們同時(shí)應(yīng)該確保每一個(gè)函數(shù)都完全正確,因?yàn)槿绻覀兘窈笕绻麑?duì)程序進(jìn)行擴(kuò)展,用到了某個(gè)函數(shù)的其他功能,而這個(gè)功能有bug的話(huà),那絕對(duì)是一件非常郁悶的事情。所以說(shuō),每編寫(xiě)完一個(gè)函數(shù)之后,都應(yīng)該對(duì)這個(gè)函數(shù)的方方面面進(jìn)行測(cè)試,這樣的測(cè)試我們稱(chēng)之為單元測(cè)試。 傳統(tǒng)的編程方式,進(jìn)行單元測(cè)試是一件很麻煩的事情,你要重新寫(xiě)另外一個(gè)程序,在該程序中調(diào)用你需要測(cè)試的方法,并且仔細(xì)觀(guān)察運(yùn)行結(jié)果,看看是否有錯(cuò)。正因 為如此麻煩,所以程序員們編寫(xiě)單元測(cè)試的熱情不是很高。于是有一個(gè)牛人推出了單元測(cè)試包,大大簡(jiǎn)化了進(jìn)行單元測(cè)試所要做的工作,這就是JUnit4。本文簡(jiǎn)要介紹一下在Eclipse3.2中使用JUnit4進(jìn)行單元測(cè)試的方法。
首先,我們來(lái)一個(gè)傻瓜式速成教程,不要問(wèn)為什么,F(xiàn)ollow Me,先來(lái)體驗(yàn)一下單元測(cè)試的快感!
首先新建一個(gè)項(xiàng)目叫JUnit_Test,我們編寫(xiě)一個(gè)Calculator類(lèi),這是一個(gè)能夠簡(jiǎn)單實(shí)現(xiàn)加減乘除、平方、開(kāi)方的計(jì)算器類(lèi),然后對(duì)這些功能進(jìn)行單元測(cè)試。這個(gè)類(lèi)并不是很完美,我們故意保留了一些Bug用于演示,這些Bug在注釋中都有說(shuō)明。該類(lèi)代碼如下:
package andycpp;
public class Calculator ...{
private static int result; // 靜態(tài)變量,用于存儲(chǔ)運(yùn)行結(jié)果
public void add(int n) ...{
result = result + n;
}
public void substract(int n) ...{
result = result - 1; //Bug: 正確的應(yīng)該是 result =result-n
}
public void multiply(int n) ...{
} // 此方法尚未寫(xiě)好
public void divide(int n) ...{
result = result / n;
}
public void square(int n) ...{
result = n * n;
}
public void squareRoot(int n) ...{
for (; ;) ; //Bug : 死循環(huán)
}
public void clear() ...{ // 將結(jié)果清零
result = 0;
}
public int getResult() ...{
return result;
}
}
第二步,將JUnit4單元測(cè)試包引入這個(gè)項(xiàng)目:在該項(xiàng)目上點(diǎn)右鍵,點(diǎn)“屬性”,如圖:
在彈出的屬性窗口中,首先在左邊選擇“Java Build Path”,然后到右上選擇“Libraries”標(biāo)簽,之后在最右邊點(diǎn)擊“Add Library…”按鈕,如下圖所示:
然后在新彈出的對(duì)話(huà)框中選擇JUnit4并點(diǎn)擊確定,如上圖所示,JUnit4軟件包就被包含進(jìn)我們這個(gè)項(xiàng)目了。
wordend 相關(guān)閱讀:
- Junit實(shí)現(xiàn)spring的單元測(cè)試
- Junit多線(xiàn)程測(cè)試的一個(gè)解決方案
- JUnit及其相關(guān)的單元測(cè)試技術(shù)
第三步,生成JUnit測(cè)試框架:在Eclipse的Package Explorer中用右鍵點(diǎn)擊該類(lèi)彈出菜單,選擇“JUnit 測(cè)試用例”。如下圖所示:
在彈出的對(duì)話(huà)框中,進(jìn)行相應(yīng)的選擇,如下圖所示:
點(diǎn)擊“下一步”后,系統(tǒng)會(huì)自動(dòng)列出你這個(gè)類(lèi)中包含的方法,選擇你要進(jìn)行測(cè)試的方法。此例中,我們僅對(duì)“加、減、乘、除”四個(gè)方法進(jìn)行測(cè)試。如下圖所示:
之后系統(tǒng)會(huì)自動(dòng)生成一個(gè)新類(lèi)CalculatorTest,里面包含一些空的測(cè)試用例。你只需要將這些測(cè)試用例稍作修改即可使用。完整的CalculatorTest代碼如下:
package andycpp;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
public class CalculatorTest ...{
private static Calculator calculator = new Calculator();
@Before
public void setUp() throws Exception ...{
calculator.clear();
}
@Test
public void testAdd() ...{
calculator.add(2);
calculator.add(3);
assertEquals(5, calculator.getResult());
}
@Test
public void testSubstract() ...{
calculator.add(10);
calculator.substract(2);
assertEquals(8, calculator.getResult());
}
@Ignore("Multiply() Not yet implemented")
@Test
public void testMultiply() ...{
}
@Test
public void testDivide() ...{
calculator.add(8);
calculator.divide(2);
assertEquals(4, calculator.getResult());
}
}
wordend 相關(guān)閱讀:
- Junit實(shí)現(xiàn)spring的單元測(cè)試
- Junit多線(xiàn)程測(cè)試的一個(gè)解決方案
- JUnit及其相關(guān)的單元測(cè)試技術(shù)
第四步,運(yùn)行測(cè)試代碼:按照上述代碼修改完畢后,我們?cè)贑alculatorTest類(lèi)上點(diǎn)右鍵,選擇“Run As à JUnit Test”來(lái)運(yùn)行我們的測(cè)試,如下圖所示:
運(yùn)行結(jié)果如下:
進(jìn)度條是紅顏色表示發(fā)現(xiàn)錯(cuò)誤,具體的測(cè)試結(jié)果在進(jìn)度條上面有表示“共進(jìn)行了4個(gè)測(cè)試,其中1個(gè)測(cè)試被忽略,一個(gè)測(cè)試失敗”。
至此,我們已經(jīng)完整體驗(yàn)了在Eclipse中使用JUnit的方法。在接下來(lái)的文章中,我會(huì)詳細(xì)解釋測(cè)試代碼中的每一個(gè)細(xì)節(jié)。
wordend 相關(guān)閱讀:
- Junit實(shí)現(xiàn)spring的單元測(cè)試
- Junit多線(xiàn)程測(cè)試的一個(gè)解決方案
- JUnit及其相關(guān)的單元測(cè)試技術(shù)
我們繼續(xù)對(duì)初級(jí)篇中的例子進(jìn)行分析。初級(jí)篇中我們使用Eclipse自動(dòng)生成了一個(gè)測(cè)試框架,在這篇文章中,我們來(lái)仔細(xì)分析一下這個(gè)測(cè)試框架中的每一個(gè)細(xì)節(jié),知其然更要知其所以然,才能更加熟練地應(yīng)用JUnit4。
一、包含必要地Package
在測(cè)試類(lèi)中用到了JUnit4框架,自然要把相應(yīng)地Package包含進(jìn)來(lái)。最主要地一個(gè)Package就是org.junit.*。把它包含進(jìn)來(lái)之后,絕大部分功能就有了。還有一句話(huà)也非常地重要“import static org.junit.Assert.*;”,我們?cè)跍y(cè)試的時(shí)候使用的一系列assertEquals方法就來(lái)自這個(gè)包。大家注意一下,這是一個(gè)靜態(tài)包含(static),是JDK5中新增添的一個(gè)功能。也就是說(shuō),assertEquals是Assert類(lèi)中的一系列的靜態(tài)方法,一般的使用方式是 Assert. assertEquals(),但是使用了靜態(tài)包含后,前面的類(lèi)名就可以省略了,使用起來(lái)更加的方便。
二、測(cè)試類(lèi)的聲明
大家注意到,我們的測(cè)試類(lèi)是一個(gè)獨(dú)立的類(lèi),沒(méi)有任何父類(lèi)。測(cè)試類(lèi)的名字也可以任意命名,沒(méi)有任何局限性。所以我們不能通過(guò)類(lèi)的聲明來(lái)判斷它是不是一個(gè)測(cè)試類(lèi),它與普通類(lèi)的區(qū)別在于它內(nèi)部的方法的聲明,我們接著會(huì)講到。
三、創(chuàng)建一個(gè)待測(cè)試的對(duì)象
你要測(cè)試哪個(gè)類(lèi),那么你首先就要?jiǎng)?chuàng)建一個(gè)該類(lèi)的對(duì)象。正如上一篇文章中的代碼:
private staticCalculator calculator =newCalculator();
為了測(cè)試Calculator類(lèi),我們必須創(chuàng)建一個(gè)calculator對(duì)象。
四、測(cè)試方法的聲明
在測(cè)試類(lèi)中,并不是每一個(gè)方法都是用于測(cè)試的,你必須使用“標(biāo)注”來(lái)明確表明哪些是測(cè)試方法。“標(biāo)注”也是JDK5的一個(gè)新特性,用在此處非常恰當(dāng)。我們可以看到,在某些方法的前有@Before、@Test、@Ignore等字樣,這些就是標(biāo)注,以一個(gè)“@”作為開(kāi)頭。這些標(biāo)注都是JUnit4自定義的,熟練掌握這些標(biāo)注的含義非常重要。
五、編寫(xiě)一個(gè)簡(jiǎn)單的測(cè)試方法
首先,你要在方法的前面使用@Test標(biāo)注,以表明這是一個(gè)測(cè)試方法。對(duì)于方法的聲明也有如下要求:名字可以隨便取,沒(méi)有任何限制,但是返回值必須為 void,而且不能有任何參數(shù)。如果違反這些規(guī)定,會(huì)在運(yùn)行時(shí)拋出一個(gè)異常。至于方法內(nèi)該寫(xiě)些什么,那就要看你需要測(cè)試些什么了。比如:
@Test
public voidtestAdd() ...{
calculator.add(2);
calculator.add(3);
assertEquals(5, calculator.getResult());
}
我們想測(cè)試一下“加法”功能時(shí)候正確,就在測(cè)試方法中調(diào)用幾次add函數(shù),初始值為0,先加2,再加3,我們期待的結(jié)果應(yīng)該是5。如果最終實(shí)際結(jié)果也是 5,則說(shuō)明add方法是正確的,反之說(shuō)明它是錯(cuò)的。assertEquals(5, calculator.getResult());就是來(lái)判斷期待結(jié)果和實(shí)際結(jié)果是否相等,第一個(gè)參數(shù)填寫(xiě)期待結(jié)果,第二個(gè)參數(shù)填寫(xiě)實(shí)際結(jié)果,也就是通過(guò)計(jì)算得到的結(jié)果。這樣寫(xiě)好之后,JUnit會(huì)自動(dòng)進(jìn)行測(cè)試并把測(cè)試結(jié)果反饋給用戶(hù)。
wordend 相關(guān)閱讀:
- Junit實(shí)現(xiàn)spring的單元測(cè)試
- Junit多線(xiàn)程測(cè)試的一個(gè)解決方案
- JUnit及其相關(guān)的單元測(cè)試技術(shù)
六、 忽略測(cè)試某些尚未完成的方法
如果你在寫(xiě)程序前做了很好的規(guī)劃,那么哪些方法是什么功能都應(yīng)該實(shí)現(xiàn)定下來(lái)。因此,即使該方法尚未完成,他的具體功能也是確定的,這也就意味著你可以為他編寫(xiě)測(cè)試用例。但是,如果你已經(jīng)把該方法的測(cè)試用例寫(xiě)完,但該方法尚未完成,那么測(cè)試的時(shí)候一定是“失敗”。這種失敗和真正的失敗是有區(qū)別的,因此JUnit 提供了一種方法來(lái)區(qū)別他們,那就是在這種測(cè)試函數(shù)的前面加上@Ignore標(biāo)注,這個(gè)標(biāo)注的含義就是“某些方法尚未完成,暫不參與此次測(cè)試”。這樣的話(huà)測(cè)試結(jié)果就會(huì)提示你有幾個(gè)測(cè)試被忽略,而不是失敗。一旦你完成了相應(yīng)函數(shù),只需要把@Ignore標(biāo)注刪去,就可以進(jìn)行正常的測(cè)試。
七、 Fixture(暫且翻譯為“固定代碼段”)
Fixture 的含義就是“在某些階段必然被調(diào)用的代碼”。比如我們上面的測(cè)試,由于只聲明了一個(gè)Calculator對(duì)象,他的初始值是0,但是測(cè)試完加法操作后,他的值就不是0了;接下來(lái)測(cè)試減法操作,就必然要考慮上次加法操作的結(jié)果。這絕對(duì)是一個(gè)很糟糕的設(shè)計(jì)!我們非常希望每一個(gè)測(cè)試都是獨(dú)立的,相互之間沒(méi)有任何耦合度。因此,我們就很有必要在執(zhí)行每一個(gè)測(cè)試之前,對(duì)Calculator對(duì)象進(jìn)行一個(gè)“復(fù)原”操作,以消除其他測(cè)試造成的影響。因此,“在任何一個(gè)測(cè)試執(zhí)行之前必須執(zhí)行的代碼”就是一個(gè)Fixture,我們用@Before來(lái)標(biāo)注它,如前面例子所示:
@Before
public void setUp() throws Exception ...{
calculator.clear();
}
這里不在需要@Test標(biāo)注,因?yàn)檫@不是一個(gè)test,而是一個(gè)Fixture。同理,如果“在任何測(cè)試執(zhí)行之后需要進(jìn)行的收尾工作”也是一個(gè)Fixture,使用@After來(lái)標(biāo)注。由于本例比較簡(jiǎn)單,沒(méi)有用到此功能。
JUnit4的一些基本知識(shí)就介紹到此,還有一些更靈活的用法放在本系列的高級(jí)篇中給大家介紹!
wordend 相關(guān)閱讀:
- Junit實(shí)現(xiàn)spring的單元測(cè)試
- Junit多線(xiàn)程測(cè)試的一個(gè)解決方案
- JUnit及其相關(guān)的單元測(cè)試技術(shù)
通過(guò)前兩篇文章,您一定對(duì)JUnit有了一個(gè)基本的了解,下面我們來(lái)探討一下JUnit4中一些高級(jí)特性。
一、 高級(jí)Fixture
上一篇文章中我們介紹了兩個(gè)Fixture標(biāo)注,分別是@Before和@After,我們來(lái)看看他們是否適合完成如下功能:有一個(gè)類(lèi)是負(fù)責(zé)對(duì)大文件(超過(guò) 500兆)進(jìn)行讀寫(xiě),他的每一個(gè)方法都是對(duì)文件進(jìn)行操作。換句話(huà)說(shuō),在調(diào)用每一個(gè)方法之前,我們都要打開(kāi)一個(gè)大文件并讀入文件內(nèi)容,這絕對(duì)是一個(gè)非常耗費(fèi)時(shí)間的操作。如果我們使用@Before和@After,那么每次測(cè)試都要讀取一次文件,效率及其低下。這里我們所希望的是在所有測(cè)試一開(kāi)始讀一次文件,所有測(cè)試結(jié)束之后釋放文件,而不是每次測(cè)試都讀文件。JUnit的作者顯然也考慮到了這個(gè)問(wèn)題,它給出了@BeforeClass 和 @AfterClass兩個(gè)Fixture來(lái)幫我們實(shí)現(xiàn)這個(gè)功能。從名字上就可以看出,用這兩個(gè)Fixture標(biāo)注的函數(shù),只在測(cè)試用例初始化時(shí)執(zhí)行 @BeforeClass方法,當(dāng)所有測(cè)試執(zhí)行完畢之后,執(zhí)行@AfterClass進(jìn)行收尾工作。在這里要注意一下,每個(gè)測(cè)試類(lèi)只能有一個(gè)方法被標(biāo)注為 @BeforeClass 或 @AfterClass,并且該方法必須是Public和Static的。
二、 限時(shí)測(cè)試
還記得我在初級(jí)篇中給出的例子嗎,那個(gè)求平方根的函數(shù)有Bug,是個(gè)死循環(huán):
public voidsquareRoot(intn) ...{
for(; ;) ; //Bug : 死循環(huán)
}
如果測(cè)試的時(shí)候遇到死循環(huán),你的臉上絕對(duì)不會(huì)露出笑容。因此,對(duì)于那些邏輯很復(fù)雜,循環(huán)嵌套比較深的程序,很有可能出現(xiàn)死循環(huán),因此一定要采取一些預(yù)防措施。限時(shí)測(cè)試是一個(gè)很好的解決方案。我們給這些測(cè)試函數(shù)設(shè)定一個(gè)執(zhí)行時(shí)間,超過(guò)了這個(gè)時(shí)間,他們就會(huì)被系統(tǒng)強(qiáng)行終止,并且系統(tǒng)還會(huì)向你匯報(bào)該函數(shù)結(jié)束的原因是因?yàn)槌瑫r(shí),這樣你就可以發(fā)現(xiàn)這些Bug了。要實(shí)現(xiàn)這一功能,只需要給@Test標(biāo)注加一個(gè)參數(shù)即可,代碼如下:
@Test(timeout = 1000)
public voidsquareRoot() ...{
calculator.squareRoot(4);
assertEquals(2, calculator.getResult());
}
Timeout參數(shù)表明了你要設(shè)定的時(shí)間,單位為毫秒,因此1000就代表1秒。
三、 測(cè)試異常
JAVA中的異常處理也是一個(gè)重點(diǎn),因此你經(jīng)常會(huì)編寫(xiě)一些需要拋出異常的函數(shù)。那么,如果你覺(jué)得一個(gè)函數(shù)應(yīng)該拋出異常,但是它沒(méi)拋出,這算不算Bug呢?這當(dāng)然是Bug,并 JUnit也考慮到了這一點(diǎn),來(lái)幫助我們找到這種Bug。例如,我們寫(xiě)的計(jì)算器類(lèi)有除法功能,如果除數(shù)是一個(gè)0,那么必然要拋出“除0異常”。因此,我們很有必要對(duì)這些進(jìn)行測(cè)試。代碼如下:
@Test(expected = ArithmeticException.class)
public void divideByZero() ...{
calculator.divide(0);
}
如上述代碼所示,我們需要使用@Test標(biāo)注的expected屬性,將我們要檢驗(yàn)的異常傳遞給他,這樣JUnit框架就能自動(dòng)幫我們檢測(cè)是否拋出了我們指定的異常。
四、 Runner (運(yùn)行器)
大家有沒(méi)有想過(guò)這個(gè)問(wèn)題,當(dāng)你把測(cè)試代碼提交給JUnit框架后,框架如何來(lái)運(yùn)行你的代碼呢?答案就是——Runner。在JUnit中有很多個(gè) Runner,他們負(fù)責(zé)調(diào)用你的測(cè)試代碼,每一個(gè)Runner都有各自的特殊功能,你要根據(jù)需要選擇不同的Runner來(lái)運(yùn)行你的測(cè)試代碼。可能你會(huì)覺(jué)得奇怪,前面我們寫(xiě)了那么多測(cè)試,并沒(méi)有明確指定一個(gè)Runner啊?這是因?yàn)镴Unit中有一個(gè)默認(rèn)Runner,如果你沒(méi)有指定,那么系統(tǒng)自動(dòng)使用默認(rèn)Runner來(lái)運(yùn)行你的代碼。換句話(huà)說(shuō),下面兩段代碼含義是完全一樣的:
import org.junit.internal.runners.TestClassRunner;
import org.junit.runner.RunWith;
//使用了系統(tǒng)默認(rèn)的TestClassRunner,與下面代碼完全一樣
public class CalculatorTest ...{
...
}
@RunWith(TestClassRunner.class)
public class CalculatorTest ...{
...
}
從上述例子可以看出,要想指定一個(gè)Runner,需要使用@RunWith標(biāo)注,并且把你所指定的Runner作為參數(shù)傳遞給它。另外一個(gè)要注意的是,@RunWith是用來(lái)修飾類(lèi)的,而不是用來(lái)修飾函數(shù)的。只要對(duì)一個(gè)類(lèi)指定了Runner,那么這個(gè)類(lèi)中的所有函數(shù)都被這個(gè)Runner來(lái)調(diào)用。最后,不要忘了包含相應(yīng)的Package哦,上面的例子對(duì)這一點(diǎn)寫(xiě)的很清楚了。接下來(lái),我會(huì)向你們展示其他Runner的特有功能。
wordend 相關(guān)閱讀:
- Junit實(shí)現(xiàn)spring的單元測(cè)試
- Junit多線(xiàn)程測(cè)試的一個(gè)解決方案
- JUnit及其相關(guān)的單元測(cè)試技術(shù)
五、 參數(shù)化測(cè)試
你可能遇到過(guò)這樣的函數(shù),它的參數(shù)有許多特殊值,或者說(shuō)他的參數(shù)分為很多個(gè)區(qū)域。比如,一個(gè)對(duì)考試分?jǐn)?shù)進(jìn)行評(píng)價(jià)的函數(shù),返回值分別為“優(yōu)秀,良好,一般,及格,不及格”,因此你在編寫(xiě)測(cè)試的時(shí)候,至少要寫(xiě)5個(gè)測(cè)試,把這5中情況都包含了,這確實(shí)是一件很麻煩的事情。我們還使用我們先前的例子,測(cè)試一下“計(jì)算一個(gè)數(shù)的平方”這個(gè)函數(shù),暫且分三類(lèi):正數(shù)、0、負(fù)數(shù)。測(cè)試代碼如下:
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());
}
}
為了簡(jiǎn)化類(lèi)似的測(cè)試,JUnit4提出了“參數(shù)化測(cè)試”的概念,只寫(xiě)一個(gè)測(cè)試函數(shù),把這若干種情況作為參數(shù)傳遞進(jìn)去,一次性的完成測(cè)試。代碼如下:
import static 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)
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},
});
}
//構(gòu)造函數(shù),對(duì)變量進(jìn)行初始化
wordend 相關(guān)閱讀:
- Junit實(shí)現(xiàn)spring的單元測(cè)試
- Junit多線(xiàn)程測(cè)試的一個(gè)解決方案
- JUnit及其相關(guān)的單元測(cè)試技術(shù)
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫(xiě)作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元
