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