JUnit是一(yi)個Java語言的(de)(de)單(dan)元測試(shi)(shi)框架(jia)。它由Kent Beck和Erich Gamma建立,逐(zhu)漸成(cheng)為(wei)源(yuan)于Kent Beck的(de)(de)sUnit的(de)(de)xUnit家族(zu)中最為(wei)成(cheng)功的(de)(de)一(yi)個。JUnit有它自己的(de)(de)JUnit擴展(zhan)生態圈。多數Java的(de)(de)開(kai)發環境(jing)都已經集成(cheng)了JUnit作為(wei)單(dan)元測試(shi)(shi)的(de)(de)工具。
JUnit是(shi)由Erich Gamma和Kent Beck編寫(xie)的(de)一(yi)個(ge)回歸測(ce)試框架(regression testing framework)。Junit測(ce)試是(shi)程(cheng)序(xu)員測(ce)試,即所謂白盒測(ce)試,因為程(cheng)序(xu)員知道(dao)被測(ce)試的(de)軟(ruan)件如何(How)完成功(gong)能和完成什么樣(What)的(de)功(gong)能。Junit是(shi)一(yi)套(tao)框架,繼(ji)承TestCase類,就可以用Junit進行自動測(ce)試了。
安裝很簡單,先(xian)到(dao)以(yi)下(xia)(xia)地址下(xia)(xia)載一個最(zui)新的zip包:
下載完以后解壓到你喜歡的目(mu)錄下,假設(she)是JUNIT_HOME,然后將JUNIT_HOME下的junit.jar包加到你的系統的CLASSPATH環境(jing)變量中(zhong),對于(yu)IDE環境(jing),對于(yu)需要用到的junit的項目(mu)增加到lib中(zhong),其設(she)置不同的IDE有(you)不同的設(she)置,這里不多講。
最簡單的范例如下:
1、創建一個(ge)TestCase的(de)子類(lei)
package junitfaq;
import java.util.*;
import junit.framework.*;
public class SimpleTest extends TestCase {
public SimpleTest(String name) {
super(name);
}
2、寫(xie)一個(ge)測試方法斷言期望的結(jie)果
public void testEmptyCollection(){
Collection collection = new ArrayList();
assertTrue(collection.isEmpty());
}
注意:JUnit推薦的做(zuo)法(fa)是以(yi)test作為待測試的方法(fa)的開頭(tou),這樣這些方法(fa)可以(yi)被自動找到并(bing)被測試。
3、寫一(yi)個(ge)suite()方法,它會使用反射(she)動(dong)態的創建一(yi)個(ge)包含所(suo)有(you)的testXxxx方法的測試套件(jian)
public static Test suite() {
return new TestSuite(SimpleTest.class);
}
4、寫(xie)一個main()方法以文本運行(xing)器(qi)的方式(shi)方便的運行(xing)測試
public static void main(String args[]) {
junit.textui.TestRunner.run(suite());
}
}
5、運行測試
以文本方式運行:
java junitfaq.SimpleTest
通過的測試結果是:
.
Time: 0
OK (1 tests)
Time上(shang)(shang)的小點表示(shi)測(ce)(ce)試個數,如果(guo)測(ce)(ce)試通過則(ze)顯示(shi)OK。否則(ze)在小點的后邊標上(shang)(shang)Fail,表示(shi)該測(ce)(ce)試失(shi)敗。
每(mei)次的測試(shi)結果都應該是(shi)OK的,這樣(yang)才能說(shuo)明測試(shi)是(shi)成功的,如果不成功就(jiu)要馬上根據提示信息進行修正了。
如(ru)果JUnit報告(gao)了測試沒有成功,它會區分失敗(failures)和錯(cuo)誤(errors)。失敗是你的(de)代(dai)碼中的(de)assert方法失敗引起的(de);而錯(cuo)誤則是代(dai)碼異常引起的(de),例如(ru)ArrayIndexOutOfBoundsException。
以圖形方式運行:
java junit.swingui.TestRunner junitfaq.SimpleTest
通(tong)過的測試(shi)結果在圖形界(jie)面的綠色(se)條部分。
以上是最簡單的(de)(de)測(ce)試(shi)樣例(li),在實(shi)際的(de)(de)測(ce)試(shi)中(zhong)我(wo)們測(ce)試(shi)某個(ge)類的(de)(de)功能是常常需要執(zhi)行一些共同的(de)(de)操作,完成以后需要銷(xiao)毀所(suo)占(zhan)用(yong)的(de)(de)資源(例(li)如網絡(luo)連接、數據庫連接,關閉打開的(de)(de)文(wen)件等),TestCase類給我(wo)們提供了setUp方(fang)法和tearDown方(fang)法,setUp方(fang)法的(de)(de)內(nei)容在測(ce)試(shi)你(ni)編寫(xie)的(de)(de)TestCase子類的(de)(de)每(mei)個(ge)testXxxx方(fang)法之(zhi)前都(dou)會(hui)運(yun)行,而tearDown方(fang)法的(de)(de)內(nei)容在每(mei)個(ge)testXxxx方(fang)法結束以后都(dou)會(hui)執(zhi)行。這個(ge)既共享了初(chu)始(shi)化(hua)代碼,又消除(chu)了各(ge)個(ge)測(ce)試(shi)代碼之(zhi)間可能產(chan)生的(de)(de)相(xiang)互影響。
不(bu)要認為(wei)壓力(li)大,就不(bu)寫測試代(dai)碼(ma)。相反編寫測試代(dai)碼(ma)會使你(ni)的壓力(li)逐漸減(jian)輕,因為(wei)通過編寫測試代(dai)碼(ma),你(ni)對類的行為(wei)有了確切的認識。你(ni)會更(geng)快地編寫出有效率地工作代(dai)碼(ma)。
下面是一些具體的編(bian)寫(xie)測試代碼(ma)的技巧或(huo)較好的實踐(jian)方法:
1.不要用TestCase的構造(zao)函數初始化Fixture,而(er)要用setUp()和tearDown()方(fang)法。
2.不要依賴(lai)或假定(ding)測試運行(xing)的順序,因為JUnit利用Vector保存測試方法。所以不同(tong)的平臺會(hui)按不同(tong)的順序從(cong)Vector中取出(chu)測試方法。
3.避免編寫有(you)副作用的TestCase。例如:如果隨后的測試(shi)依賴于某些特(te)定的交易數(shu)(shu)據,就(jiu)不(bu)要提交交易數(shu)(shu)據。簡單的回滾就(jiu)可(ke)以了。
4.當繼承一個(ge)測試(shi)類時(shi),記得調(diao)用父類的setUp()和(he)tearDown()方法。
5.將測試代碼(ma)和工作代碼(ma)放在一起,一邊同步(bu)編譯(yi)和更新(xin)。(使用Ant中有支持junit的task.)
6.測試(shi)類和測試(shi)方法應該有(you)一(yi)致的命名方案。如在(zai)工作類名前(qian)加上test從而形(xing)成測試(shi)類名。
7.確保測(ce)試與時間無關,不(bu)要依(yi)賴使用過期(qi)的數據進行測(ce)試。導(dao)致在(zai)隨后的維護過程中很難重(zhong)現測(ce)試。
8.如(ru)果你(ni)編寫的(de)軟(ruan)件面(mian)向國際(ji)市場(chang),編寫測試(shi)時要考慮國際(ji)化(hua)的(de)因素。不要僅用母語(yu)的(de)Locale進(jin)行測試(shi)。
9.盡可能地利用JUnit提供地assert/fail方法以及(ji)異常處理的方法,可以使代碼(ma)更為簡(jian)潔。
10.測試要盡可能地小(xiao),執行速度快。
11.不要硬性規定數據文件的路徑(jing)。
12.利用(yong)Junit的自(zi)動異常處(chu)理(li)書寫簡潔的測(ce)試(shi)代碼
事實上(shang)在Junit中使用try-catch來捕獲異常(chang)是沒(mei)有必要(yao)的,Junit會自動捕獲異常(chang)。那些沒(mei)有被(bei)捕獲的異常(chang)就被(bei)當(dang)成錯誤處(chu)理。
13.充分(fen)利用Junit 的assert/fail方法
assertSame()用來測試兩個引(yin)用是(shi)否(fou)指向同一個對(dui)象
assertEquals()用(yong)來測試兩個對象是否相等
14.確保測試代碼與時間無關
15.使用文檔生成器做(zuo)測試文檔。
JUnit和ant結合(he)
ant提供了兩個target:junit和junitreport運行(xing)所有測試用例,并生成html格式的報(bao)表
具體操作如下:
1.將 junit.jar 放(fang)在 ANT_HOMElib 目錄下
2.修改 build.xml,加入如下 內容:
-------------- One or more tests failed, check the report for detail... -----------------------------
運行這個target,ant會運行每個TestCase,在report目(mu)錄下(xia)就(jiu)有了很(hen)多(duo)TEST*.xml和(he)一些網頁打開(kai)report目(mu)錄下(xia)的 index.html就(jiu)可以(yi)看到很(hen)直觀的測試運行報告,一目(mu)了然。
在Eclipse中開發、運行JUnit測(ce)試相當(dang)簡單。因為Eclipse本身集成了JUnit相關(guan)組件,并對JUnit的運行提供了無(wu)縫的支持。
junit3.x
我們通常(chang)使用(yong)junit 3.8
(1)、使(shi)用(yong)junit3.x版(ban)本進(jin)行單元測試時(shi),測試類必須要(yao)繼承于TestCase父類;
(2)、測試方法需(xu)要遵循的原則(ze):
A、public的
B、void的
C、無方法參數
D、方法名(ming)稱(cheng)必須(xu)以(yi)test開頭
(3)、不同的Test Case之間一定要(yao)保持完全的獨立性,不能有任何(he)的關聯。
(4)、我們要掌握好測試方(fang)法的順序,不能依賴(lai)于測試方(fang)法自己的執行順序。
demo:
public class TestMyNumber extends TestCase {
private MyNumber myNumber;
public TestMyNumber(String name) {
super(name);
}
// 在每個測(ce)試方法執行 [之前] 都會被調用
@Override
public void setUp() throws Exception {
// System.out.println("歡(huan)迎使用(yong)Junit進行單元測試(shi)…");
myNumber = new MyNumber();
}
// 在每個測試方法執行 [之后] 都(dou)會被調用
@Override
public void tearDown() throws Exception {
// System.out.println("Junit單元(yuan)測(ce)試結(jie)束…");
}
public void testDivideByZero() {
Throwable te = null;
try {
myNumber.divide(6, 0);
Assert.fail("測試失敗");
} catch (Exception e) {
e.printStackTrace();
te = e;
}
Assert.assertEquals(Exception.class, te.getClass());
Assert.assertEquals("除(chu)數不能為(wei) 0 ", te.getMessage());
}
}
junit4.x
(1)、使用junit4.x版本進行單元測試時,不(bu)用測試類繼承TestCase父(fu)類,因(yin)為,junit4.x全面引入了(le)Annotation來執(zhi)行我們(men)編寫的(de)測試。
(2)、junit4.x版本(ben),引用了(le)注解的方式,進行單元測(ce)試;
(3)、junit4.x版本我們常用(yong)的注解:
A、@Before 注解:與(yu)junit3.x中的setUp()方法功能一樣,在(zai)每個測(ce)試方法之前執行;
B、@After 注解:與junit3.x中的tearDown()方(fang)(fang)法(fa)功(gong)能一樣,在(zai)每個測試方(fang)(fang)法(fa)之后(hou)執行;
C、@BeforeClass 注解(jie):在所有方法執(zhi)(zhi)行之前執(zhi)(zhi)行;
D、@AfterClass 注解:在所有方法執(zhi)行之后執(zhi)行;
E、@Test(timeout=xxx)注解(jie):設置(zhi)當前測試方法在一定時(shi)間(jian)內(nei)運(yun)行完,否則返回錯(cuo)誤;
F、@Test(expected=Exception.class)注解:設置被測試的方(fang)法是否有異(yi)常拋(pao)出(chu)。拋(pao)出(chu)異(yi)常類(lei)型(xing)為:Exception.class;
G、@Ignore注解:注釋掉(diao)一(yi)(yi)個(ge)測試方法(fa)(fa)或(huo)一(yi)(yi)個(ge)類,被注釋的(de)方法(fa)(fa)或(huo)類,不(bu)會(hui)被執行。
demo:
package com.an.junit;
import static org.junit.Assert.*;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
public class TestMyNumber {
private MyNumber myNumber;
@BeforeClass
// 在所(suo)有方法執行(xing)之前執行(xing)
public static void globalInit() {
System.out.println("init all method...");
}
@AfterClass
// 在所有方法執行之后執行
public static void globalDestory() {
System.out.println("destory all method...");
}
@Before
// 在每個測試方(fang)法之前執行
public void setUp() {
System.out.println("start setUp method");
myNumber = new MyNumber();
}
@After
// 在每個測試方法之后執行
public void tearDown() {
System.out.println("end tearDown method");
}
@Test(timeout=600)// 設置(zhi)限定測(ce)試方(fang)法的(de)運行(xing)時(shi)間 如果超出則返回錯(cuo)誤
public void testAdd() {
System.out.println("testAdd method");
int result = myNumber.add(2, 3);
assertEquals(5, result);
}
@Test
public void testSubtract() {
System.out.println("testSubtract method");
int result = myNumber.subtract(1, 2);
assertEquals(-1, result);
}
@Test
public void testMultiply() {
System.out.println("testMultiply method");
int result = myNumber.multiply(2, 3);
assertEquals(6, result);
}
@Test
public void testDivide() {
System.out.println("testDivide method");
int result = 0;
try {
result = myNumber.divide(6, 2);
} catch (Exception e) {
fail();
}
assertEquals(3, result);
}
@Test(expected = Exception.class)
public void testDivide2() throws Exception {
System.out.println("testDivide2 method");
myNumber.divide(6, 0);
fail("test Error");
}
public static void main(String[] args) {
}
}
另外junit是(shi)(shi)在(zai)(zai)極限編程(cheng)和重(zhong)構(refactor)中被極力推薦使(shi)用的(de)工具,因為在(zai)(zai)實現自動單元(yuan)測(ce)試的(de)情況下可(ke)以大大的(de)提高(gao)開發的(de)效(xiao)率,但是(shi)(shi)實際上編寫測(ce)試代碼也(ye)是(shi)(shi)需(xu)要耗費(fei)很多的(de)時(shi)間(jian)和精力的(de),那(nei)么使(shi)用這(zhe)個(ge)東西好處到底在(zai)(zai)哪里呢?筆者認為是(shi)(shi)這(zhe)樣的(de):
極限編程
要求(qiu)在(zai)編(bian)寫(xie)代(dai)(dai)(dai)(dai)碼(ma)(ma)(ma)之前先(xian)寫(xie)測(ce)試(shi),這樣可以(yi)強制你在(zai)寫(xie)代(dai)(dai)(dai)(dai)碼(ma)(ma)(ma)之前好(hao)好(hao)的思(si)考(kao)代(dai)(dai)(dai)(dai)碼(ma)(ma)(ma)(方法)的功(gong)能和(he)邏輯,否則編(bian)寫(xie)的代(dai)(dai)(dai)(dai)碼(ma)(ma)(ma)很不穩定,那么你需要同時維護測(ce)試(shi)代(dai)(dai)(dai)(dai)碼(ma)(ma)(ma)和(he)實際代(dai)(dai)(dai)(dai)碼(ma)(ma)(ma),這個工作量就會大大增(zeng)(zeng)加。因此在(zai)極限編(bian)程(cheng)中(zhong),基本過程(cheng)是這樣的:構思(si)-> 編(bian)寫(xie)測(ce)試(shi)代(dai)(dai)(dai)(dai)碼(ma)(ma)(ma)-> 編(bian)寫(xie)代(dai)(dai)(dai)(dai)碼(ma)(ma)(ma)-> 測(ce)試(shi),而且(qie)編(bian)寫(xie)測(ce)試(shi)和(he)編(bian)寫(xie)代(dai)(dai)(dai)(dai)碼(ma)(ma)(ma)都是增(zeng)(zeng)量式的,寫(xie)一(yi)點測(ce)一(yi)點,在(zai)編(bian)寫(xie)以(yi)后的代(dai)(dai)(dai)(dai)碼(ma)(ma)(ma)中(zhong)如(ru)果發現問題(ti)可以(yi)較快的追蹤到問題(ti)的原因,減小回歸錯誤的糾(jiu)錯難度。
重構
其好處(chu)和極限(xian)編程中是類似的,因為重構也(ye)是要(yao)求改(gai)一點測一點,減少回歸錯誤造成的時間消耗。
其他情況
我(wo)們在開發的(de)(de)時(shi)候使用junit寫(xie)一(yi)些(xie)適當(dang)的(de)(de)測試(shi)也是(shi)(shi)有必要(yao)的(de)(de),因(yin)為(wei)一(yi)般我(wo)們也是(shi)(shi)需(xu)要(yao)編寫(xie)測試(shi)的(de)(de)代碼(ma)的(de)(de),可(ke)能(neng)原(yuan)來不是(shi)(shi)使用的(de)(de)junit,如(ru)(ru)果(guo)(guo)(guo)使用junit,而且針對接口(方法(fa))編寫(xie)測試(shi)代碼(ma)會(hui)減少以后(hou)(hou)(hou)的(de)(de)維(wei)護工(gong)作,例(li)如(ru)(ru)以后(hou)(hou)(hou)對方法(fa)內部的(de)(de)修改(這個就(jiu)是(shi)(shi)相(xiang)當(dang)于重構的(de)(de)工(gong)作了)。另外就(jiu)是(shi)(shi)因(yin)為(wei)junit有斷言功能(neng),如(ru)(ru)果(guo)(guo)(guo)測試(shi)結(jie)(jie)果(guo)(guo)(guo)不通過(guo)(guo)會(hui)告訴(su)我(wo)們哪個測試(shi)不通過(guo)(guo),為(wei)什么,而如(ru)(ru)果(guo)(guo)(guo)是(shi)(shi)像以前的(de)(de)一(yi)般做法(fa)是(shi)(shi)寫(xie)一(yi)些(xie)測試(shi)代碼(ma)看(kan)其輸出結(jie)(jie)果(guo)(guo)(guo),然后(hou)(hou)(hou)再(zai)由自己來判(pan)斷結(jie)(jie)果(guo)(guo)(guo)是(shi)(shi)否(fou)正確,使用junit的(de)(de)好處就(jiu)是(shi)(shi)這個結(jie)(jie)果(guo)(guo)(guo)是(shi)(shi)否(fou)正確的(de)(de)判(pan)斷是(shi)(shi)它來完成的(de)(de),我(wo)們只(zhi)需(xu)要(yao)看(kan)看(kan)它告訴(su)我(wo)們結(jie)(jie)果(guo)(guo)(guo)是(shi)(shi)否(fou)正確就(jiu)可(ke)以了,在一(yi)般情況下(xia)會(hui)大大提(ti)高效率。
JUnit是(shi)一個(ge)開放源代碼的Java測(ce)試(shi)框架(jia)(jia),用(yong)(yong)(yong)于(yu)編寫和運行可(ke)重(zhong)復的測(ce)試(shi)。他是(shi)用(yong)(yong)(yong)于(yu)單(dan)元(yuan)測(ce)試(shi)框架(jia)(jia)體系xUnit的一個(ge)實例(用(yong)(yong)(yong)于(yu)java語(yu)言)。它包括以下特性:
1、用于測試期(qi)望(wang)結果的斷(duan)言(Assertion)
2、用于共享共同測試(shi)數據的測試(shi)工具(ju)
3、用(yong)于方便的組織和運行測試(shi)的測試(shi)套件
4、圖形和文(wen)本的測(ce)試運(yun)行器