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