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