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