循序漸進(jìn)學(xué)習(xí)JUnit
作者:Michel Casabianca
使用最流行的開放資源測(cè)試框架之一學(xué)習(xí)單元測(cè)試基礎(chǔ)。
使用JUnit可以大量減少Java代碼中程序錯(cuò)誤的個(gè)數(shù),JUnit是一種流行的單元測(cè)試框架,用于在發(fā)布代碼之前對(duì)其進(jìn)行單元測(cè)試。現(xiàn)在讓我們來詳細(xì)研究如何使用諸如JUnit、Ant和Oracle9i JDeveloper等工具來編寫和運(yùn)行單元測(cè)試。
為什么使用JUnit?
多數(shù)開發(fā)人員都同意在發(fā)布代碼之前應(yīng)當(dāng)對(duì)其進(jìn)行測(cè)試,并利用工具進(jìn)行回歸(regression)測(cè)試。做這項(xiàng)工作的一個(gè)簡單方法是在所有Java類中以main()方法實(shí)施測(cè)試。例如,假設(shè)使用ISO格式(這意味著有一個(gè)以這一格式作為參數(shù)的構(gòu)造器和返回一個(gè)格式化的ISO字符串的toString()方法)以及一個(gè)GMT時(shí)區(qū)來編寫一個(gè)Date的子類。清單1 就是這個(gè)類的一個(gè)簡單實(shí)現(xiàn)。
不過,這種測(cè)試方法并不需要單元測(cè)試限定語(qualifier),原因如下:
- 在一個(gè)類中進(jìn)行測(cè)試的最小單元是方法,你應(yīng)當(dāng)對(duì)每個(gè)方法進(jìn)行單獨(dú)測(cè)試,以準(zhǔn)確地找出哪些方法工作正常,哪些方法工作不正常。
- 即使前面的測(cè)試失敗,也應(yīng)當(dāng)對(duì)各個(gè)方法進(jìn)行測(cè)試。在此實(shí)施中,如果單個(gè)測(cè)試失敗,后面的測(cè)試將根本不會(huì)運(yùn)行。這就意味著你不會(huì)知道不良代碼在你的實(shí)施中所占的百分比。
- 測(cè)試代碼會(huì)出現(xiàn)在生成的類中。這在類的大小方面可能不是什么問題,但卻可能會(huì)成為安全性因素之一:例如,如果你的測(cè)試嵌入了數(shù)據(jù)庫連接密碼,那么這一信息將很容易用于已發(fā)布的類中。
- 沒有框架可以自動(dòng)啟動(dòng)這一測(cè)試,你必須編寫一個(gè)腳本來啟動(dòng)每一個(gè)測(cè)試。
- 在編寫一個(gè)報(bào)告時(shí),你必須編寫自己的實(shí)現(xiàn),并定義規(guī)則,以方便地報(bào)告錯(cuò)誤。
JUnit框架就是設(shè)計(jì)用來解決這些問題的。這一框架主要是所有測(cè)試實(shí)例(稱為"TestCase")的一個(gè)父類,并提供工具來運(yùn)行所編寫的測(cè)試、生成報(bào)告及定義測(cè)試包(test suite)。
讓我們?yōu)?/FONT>IsoDate類編寫一個(gè)測(cè)試:這個(gè)IsoDateTest類類似于:
import java.text.ParseException; import junit.framework.TestCase; /** * Test case for <code>IsoDate</code>. */ public class IsoDateTest extends TestCase { public void testIsoDate() throws Exception { IsoDate epoch=new IsoDate( "1970-01-01 00:00:00 GMT"); assertEquals(0,epoch.getTime()); IsoDate eon=new IsoDate( "2001-09-09 01:46:40 GMT"); assertEquals( 1000000000L*1000,eon.getTime()); } public void testToString() throws ParseException { IsoDate epoch=new IsoDate(0); assertEquals("1970-01-01 00:00:00 GMT",epoch.toString()); IsoDate eon=new IsoDate( 1000000000L*1000); assertEquals("2001-09-09 01:46:40 GMT",eon.toString()); } }
本例中要注意的重點(diǎn)是已經(jīng)編寫了一個(gè)用于測(cè)試的獨(dú)立類,因此可以對(duì)這些文件進(jìn)行過濾,以避免將這一代碼嵌入到將要發(fā)布的文檔中。另外,本例還為你希望在你的代碼中測(cè)試的每個(gè)方法編寫了一個(gè)專用測(cè)試方法,因此你將確切地知道需要對(duì)哪些方法進(jìn)行測(cè)試、哪些方法工作正常以及哪些方法工作不正常。如果在編寫實(shí)施文檔之前已經(jīng)編寫了該測(cè)試,你就可以利用它來衡量工作的進(jìn)展情況。
安裝并運(yùn)行JUnit
要運(yùn)行此示例測(cè)試實(shí)例,必須首先下載并安裝JUnit。JUnit的最新版本可以在JUnit的網(wǎng)站 www.junit.org免費(fèi)下載。該軟件包很小(約400KB),但其中包括了源代碼和文檔。要安裝此程序,應(yīng)首先對(duì)該軟件包進(jìn)行解壓縮(junitxxx.zip)。它將創(chuàng)建一個(gè)目錄(junitxxx),在此目錄下有文檔(在doc目錄中)、框架的應(yīng)用編程接口(API)文檔(在javadoc目錄中)、運(yùn)行程序的庫文件(junit.jar)以及示例測(cè)試實(shí)例(在junit目錄中)。截至我撰寫本文時(shí),JUnit的最新版本為3.8.1,我是在此版本上對(duì)示例進(jìn)行測(cè)試的。
![]() |
要運(yùn)行此測(cè)試實(shí)例,將源文件(IsoDate.java和IsoDateTest.java)拷貝到Junit的安裝目錄下,打開終端,進(jìn)入該目錄,然后輸入以下命令行(如果你正在使用UNIX):
export CLASSPATH=.:./junit.jar javac *.java 或者,如果你正在Windows,輸入以下命令行 set CLASSPATH=.;junit.jar javac *.java
這些命令行對(duì)CLASSPATH進(jìn)行設(shè)置,使其包含當(dāng)前目錄中的類和junit.jar庫,并編譯Java源文件。
要在終端上運(yùn)行該測(cè)試,輸入以下命令行:
java junit.textui.TestRunner IsoDateTest
此命令行將運(yùn)行該測(cè)試,并在圖 1所示的控制臺(tái)上顯示測(cè)試結(jié)果。
才在此工具可以運(yùn)行類名被傳遞到命令行中的單個(gè)測(cè)試。注意:只有對(duì)命令行的最后測(cè)試才在考慮之內(nèi),以前的測(cè)試都被忽略了。(看起來像一個(gè)程序錯(cuò)誤,是吧?)
JUnit還提供了利用AWT(抽象窗口工具包)或Swing運(yùn)行測(cè)試的圖形界面。為了利用此圖形界面運(yùn)行測(cè)試,在終端上輸入以下命令行:
java junit.awtui.TestRunner IsoDateTest
或者使用Swing界面:
java junit.swingui.TestRunner IsoDateTest
此命令行將顯示圖 2所示的界面。要選擇一個(gè)測(cè)試并使其運(yùn)行,點(diǎn)擊帶有三個(gè)點(diǎn)的按鈕。這將顯示CLASSPATH(還有測(cè)試包,但我們將在后面討論)中所有測(cè)試的列表。要運(yùn)行測(cè)試,點(diǎn)擊"Run"按鈕。測(cè)試應(yīng)當(dāng)正確運(yùn)行,并在圖 2所示的界面中顯示結(jié)果。
在此界面中你應(yīng)當(dāng)選中復(fù)選框"Reload Classes Every Run",以便運(yùn)行器在運(yùn)行測(cè)試類之前對(duì)它們進(jìn)行重新加載。這樣就可以方便地編輯、編譯并運(yùn)行測(cè)試,而不需要每次都啟動(dòng)圖形界面。
在該復(fù)選框下面是一個(gè)進(jìn)度條,在運(yùn)行較大的測(cè)試包時(shí),該進(jìn)度條非常有用。運(yùn)行的測(cè)試、錯(cuò)誤和失敗的數(shù)量都會(huì)在進(jìn)度條下面顯示出來。再下面是一個(gè)失敗列表和一個(gè)測(cè)試層次結(jié)構(gòu)。失敗消息顯示在底部。通過點(diǎn)擊Test Hierarchy(測(cè)試層次結(jié)構(gòu))面板,然后再點(diǎn)擊窗口右上角的"Run"按鈕,即可運(yùn)行單個(gè)測(cè)試方法。請(qǐng)記住,使用命令行工具是不可能做到這些的。
注意,當(dāng)運(yùn)行工具來啟動(dòng)測(cè)試類時(shí),這些類必須存在于CLASSPATH中。但是如果測(cè)試類存儲(chǔ)在jar文件中,那么即使這些jar文件存在于CLASSPATH中,JUnit也不能找到這些測(cè)試類。
![]() |
這并不是一種啟動(dòng)測(cè)試的方便方法,但幸運(yùn)的是,JUnit已經(jīng)被集成到了其他工具(如Ant和Oracle9i JDeveloper)中,以幫助你開發(fā)測(cè)試并使測(cè)試能夠自動(dòng)運(yùn)行。
編寫Junit測(cè)試實(shí)例
你已經(jīng)看到了測(cè)試類的源代碼對(duì)IsoDate實(shí)施進(jìn)行了詢問。現(xiàn)在讓我們來研究這樣的測(cè)試文件的實(shí)施。
測(cè)試實(shí)例由junit.frameword.TestCase繼承而來是為了利用JUnit框架的優(yōu)點(diǎn)。這個(gè)類的名字就是在被測(cè)試類的名字上附加"Test"。因?yàn)槟阏跍y(cè)試一個(gè)名為IsoDate的類,所以其測(cè)試類的名字就是IsoDateTest。為了訪問除私有方法之外的所有方法,這個(gè)類通常與被測(cè)類在同一個(gè)包中。
注意,你必須為你希望測(cè)試的在類中定義的每個(gè)方法都編寫一個(gè)方法。你要測(cè)試構(gòu)造器或使用了ISO日期格式的方法,因此你將需要為以ISO格式的字符串作為參數(shù)的構(gòu)造器和toString()方法編寫一個(gè)測(cè)試方法。其命名方式與測(cè)試類的命名方式類似:在被測(cè)試方法(或構(gòu)造器)前面附加"test"。
測(cè)試方法的主體通過驗(yàn)證assertion(斷言)對(duì)被測(cè)方法進(jìn)行詢問。例如,在toString()實(shí)施的測(cè)試方法中,你希望確認(rèn)該方法已經(jīng)對(duì)時(shí)間的設(shè)定進(jìn)行了很好的說明(對(duì)于UNIX系統(tǒng)來說,最初問世的時(shí)間為1970年1月1日的午夜)。要實(shí)施assertion,你可以使用Junit框架提供的assertion方法。這些方法在該框架的junit.framework.Assert類中被實(shí)施,并且可以在你的測(cè)試中被訪問,這是因?yàn)锳ssert是TestCase的父類。這些方法可與Java中的關(guān)鍵字assert(是在J2EE 1.4中新出現(xiàn)的)相比。一些assertion方法可以檢查原始類型(如布爾型、整型等)之間或?qū)ο笾g是否相等(利用equals()方法檢查兩個(gè)對(duì)象是否相等)。其他assertion方法檢查兩個(gè)對(duì)象是否相同、一個(gè)對(duì)象是否為"空"或"非空",以及一個(gè)布爾值(通常由一個(gè)表達(dá)式生成)是"真"還是"假"。在表 1中對(duì)這些方法進(jìn)行了總結(jié)。
對(duì)于那些采用浮點(diǎn)類型或雙精度類型參數(shù)的assertion,存在一個(gè)第三種方法,即采用一個(gè)delta值作為參數(shù)進(jìn)行比較。另外還要注意,assertEquals()和assertSame()方法一般不會(huì)產(chǎn)生相同的結(jié)果。(兩個(gè)具有相同值的字符串可以不相同,因?yàn)樗鼈兪莾蓚€(gè)具有不同內(nèi)存地址的不同對(duì)象。)因此,assertEquals()將會(huì)驗(yàn)證assertion的有效性,而assertSame()則不會(huì)。注意,對(duì)于表 1 中的每個(gè)assertion方法,你還有一種選擇,就是引入另一個(gè)參數(shù),如果assertion失敗,該參數(shù)就會(huì)給出一條解釋性消息。例如,assertEquals(int 期望值, int 實(shí)際值)就可以與一個(gè)諸如assertEquals(字符串消息,int期望值,int實(shí)際值)的消息一起使用。
當(dāng)一個(gè)assertion失敗時(shí),該assertion方法會(huì)拋出一個(gè)AssertFailedError或ComparisonFailure。AssertionFailedError由java.lang.Error繼承而來,因此你不必在測(cè)試方法的throws語句中對(duì)其進(jìn)行聲明。而ComparisonFailure由AssertionFailedError繼承而來,因此你也不必對(duì)其進(jìn)行聲明。因?yàn)楫?dāng)一個(gè)assertion失敗時(shí)會(huì)在測(cè)試方法中拋出一個(gè)錯(cuò)誤,所以后面的assertion將不會(huì)繼續(xù)運(yùn)行。框架捕捉到這些錯(cuò)誤并認(rèn)定該測(cè)試已經(jīng)失敗后,就會(huì)打印出一條說明錯(cuò)誤的消息。這個(gè)消息由assertion生成,并且被傳遞到assertion方法(如果有的話)。
現(xiàn)在將下面一行語句添加到testIsoDate()方法的末尾:
assertEquals("This is a test",1,2);
現(xiàn)在編譯并運(yùn)行測(cè)試:
$ javac *.java $ java junit.textui.TestRunner IsoDateTest .F. Time: 0,348 There was 1 failure: 1) testIsoDate(IsoDateTest)junit.framework .AssertionFailedError: This is a test expected:<1> but was:<2> at IsoDateTest.testIsoDate (IsoDateTest.java:29) FAILURES!!! Tests run: 2, Failures: 1, Errors: 0
JUnit為每個(gè)已處理的測(cè)試打印一個(gè)點(diǎn),顯示字母"F"來表示失敗,并在assertion失敗時(shí)顯示一條消息。此消息由你發(fā)送到assertion方法的注釋和assertion的結(jié)果組成(自動(dòng)生成)。從這里可以看出assertion方法的參數(shù)順序?qū)τ谏傻南⒎浅V匾5谝粋€(gè)參數(shù)是期望值,而第二個(gè)參數(shù)則是實(shí)際值。
如果在測(cè)試方法中出現(xiàn)了某種錯(cuò)誤(例如,拋出了一個(gè)異常),該工具就會(huì)將其顯示為一個(gè)錯(cuò)誤(而不是由assertion失敗而產(chǎn)生的一個(gè)"失敗")。現(xiàn)在對(duì)IsoDateTest類進(jìn)行修改,以將前面增加的一行語句用以下語句代替:
throw new Exception("This is a test");
然后編譯并運(yùn)行測(cè)試:
$ javac *.java $ java junit.textui.TestRunner IsoDateTest .E. Time: 0,284 There was 1 error: 1) testIsoDate(IsoDateTest)java.lang. Exception: This is a test at IsoDate Test.testIsoDate(IsoDateTest.java:30) FAILURES!!! Tests run: 2, Failures: 0, Errors: 1
該工具將該異常顯示為一個(gè)錯(cuò)誤。因此,一個(gè)錯(cuò)誤表示一個(gè)錯(cuò)誤的測(cè)試方法,而不是表示一個(gè)錯(cuò)誤的測(cè)試實(shí)施。
Assert類還包括一個(gè)fail()方法(該版本帶有解釋性消息),該方法將通過拋出AssertionFailedError來中斷正在運(yùn)行的測(cè)試。當(dāng)你希望一個(gè)測(cè)試失敗而不會(huì)調(diào)用一個(gè)判定方法時(shí),fail()方法是非常有用的。例如,如果一段代碼應(yīng)當(dāng)拋出一個(gè)異常而未拋出,那么可以調(diào)用fail()方法使該測(cè)試失敗,方法如下:
public void testIndexOutOfBounds() { try { ArrayList list=new ArrayList(); list.get(0); fail("IndexOutOfBoundsException not thrown"); } catch(IndexOutOfBoundsException e) {} }
JUnit的高級(jí)特性
在示例測(cè)試實(shí)例中,你已經(jīng)同時(shí)運(yùn)行了所有的測(cè)試。在現(xiàn)實(shí)中,你可能希望運(yùn)行一個(gè)給定的測(cè)試方法來詢問你正編寫的實(shí)施方法,所以你需要定義一組要運(yùn)行的測(cè)試。這就是框架的junit.framework.TestSuite類的目的,這個(gè)類其實(shí)只是一個(gè)容器,你可以向其中添加一系列測(cè)試。如果你正在進(jìn)行toString()實(shí)施,并希望運(yùn)行相應(yīng)的測(cè)試方法,那么你可以通過重寫測(cè)試的suite()方法來通知運(yùn)行器,方法如下:
public static Test suite() { TestSuite suite= new TestSuite(); suite.addTest(new IsoDateTest ("testToString")); return suite; }
在此方法中,你用具體示例說明了一個(gè)TestSuite對(duì)象,并向其中添加了測(cè)試。為了在方法級(jí)定義測(cè)試,你可以利用構(gòu)造器將方法名作為參數(shù)使測(cè)試類實(shí)例化。此構(gòu)造器可按如下方法實(shí)施:
public IsoDateTest(String name) { super(name); }
將上面的構(gòu)造器和方法添加到IsoDateTest類(還需要引入junit.framework.Test和junit.framework.TestSuite),并在終端上輸入:
![]() |
圖3:選擇一個(gè)測(cè)試方法
$ javac *.java $ java junit.textui.TestRunner IsoDateTest . Time: 0,31 OK (1 test)
注意,在添加到測(cè)試包中的測(cè)試方法中,只運(yùn)行了一個(gè)測(cè)試方法,即toString()方法。
你也可以利用圖形界面,通過在圖3所示的Test Hierarchy面板中選擇測(cè)試方法來運(yùn)行一個(gè)給定的測(cè)試方法。但是,要注意當(dāng)整個(gè)測(cè)試包被運(yùn)行一次后,該面板將被填滿。
當(dāng)你希望將一個(gè)測(cè)試實(shí)例中的所有測(cè)試方法添加到一個(gè)TestSuite對(duì)象中時(shí),可以使用一個(gè)專用構(gòu)造器,該構(gòu)造器將此測(cè)試實(shí)例的類對(duì)象作為參數(shù)。例如,你可以使用IsoDateTest類實(shí)施suite()方法,方法如下:
public static Test suite() { return new TestSuite(IsoDateTest.class); }
還有一些情況,你可能希望運(yùn)行一組由其他測(cè)試(如在工程發(fā)布之前的所有測(cè)試)組成的測(cè)試。在這種情況下,你必須編寫一個(gè)實(shí)施suite()方法的類,以建立希望運(yùn)行的測(cè)試包。例如,假定你已經(jīng)編寫了測(cè)試類Atest和Btest。為了定義那些包含了類ATest中的所有測(cè)試和在BTest中定義的測(cè)試包的集合,可以編寫下面的類:
import junit.framework.*; /** * TestSuite that runs all tests. */ public class AllTests { public static Test suite() { TestSuite suite= new TestSuite("All Tests"); suite.addTestSuite(ATest.class); suite.addTest(BTest.suite()); return suite; } }
你完全可以像運(yùn)行單個(gè)測(cè)試實(shí)例那樣運(yùn)行這個(gè)測(cè)試包。注意,如果一個(gè)測(cè)試在一個(gè)套件中添加了兩次,那么運(yùn)行器將運(yùn)行它兩次(測(cè)試包和運(yùn)行器都不會(huì)檢查該測(cè)試是否是唯一的)。為了了解實(shí)際的測(cè)試包的實(shí)施,應(yīng)當(dāng)研究Junit本身的測(cè)試包。這些類的源代碼存在于JUnit安裝的junit/test目錄下。
![]() |
將一個(gè)main()方法添加到一個(gè)測(cè)試或一個(gè)測(cè)試包中有時(shí)是非常方便的,因此可以在不使用運(yùn)行器的情況下啟動(dòng)測(cè)試。例如,要將AllTests測(cè)試包作為一個(gè)標(biāo)準(zhǔn)的Java程序啟動(dòng),可以將下面的main()方法添加到類中:
public static void main(String[] args) { junit.textui.TestRunner.run(suite()); }
現(xiàn)在可以通過輸入java AllTests來啟動(dòng)這個(gè)測(cè)試包。
JUnit框架還提供了一種有效利用代碼的方法,即將資源集合到被稱為fixture的對(duì)象集中。例如,該示例測(cè)試實(shí)例利用兩個(gè)叫作epoch和eon的參考日期。將這些日期重新編譯到每個(gè)方法測(cè)試中只是浪費(fèi)時(shí)間(而且還可能出現(xiàn)錯(cuò)誤)。你可以用fixture重新編寫測(cè)試,如清單2所示。
你定義了兩個(gè)參考日期,作為測(cè)試類的段,并將它們編譯到一個(gè)setUp()方法中。這一方法在每個(gè)測(cè)試方法之前被調(diào)用。與其對(duì)應(yīng)的方法是tearDown()方法,它將在每個(gè)測(cè)試方法運(yùn)行之后清除所有的資源(在這個(gè)實(shí)施中,該方法事實(shí)上什么也沒做,因?yàn)槔占鳛槲覀兺瓿闪诉@項(xiàng)工作)。現(xiàn)在編譯這個(gè)測(cè)試實(shí)例(其源代碼應(yīng)當(dāng)放在JUnit的安裝目錄中)并運(yùn)行它:
$ javac *.java $ java junit.textui.TestRunner IsoDateTest2 .setUp() testIsoDate() tearDown() .setUp() testToString() tearDown() Time: 0,373 OK (2 tests)
注意:在該測(cè)試實(shí)例中建立了參考日期,因此在任何測(cè)試方法中修改這些日期都不會(huì)對(duì)其他測(cè)試產(chǎn)生不利影響。你可以將代碼放到這兩個(gè)方法中,以建立和釋放每個(gè)測(cè)試所需要的資源(如數(shù)據(jù)庫連接)。
JUnit發(fā)布版還提供了擴(kuò)展模式(在包junit.extensions中),即test decor-ators,以提供像重復(fù)運(yùn)行一個(gè)給定的測(cè)試這樣的新功能。它還提供了一個(gè)TestSuite,以方便你在獨(dú)立的線程中同時(shí)運(yùn)行所有測(cè)試,并在所有線程中的測(cè)試都完成時(shí)停止。
利用Ant使測(cè)試自動(dòng)化 如前面所述,測(cè)試運(yùn)行器是非常原始的。如果你正在運(yùn)行Ant來編譯你的工程,那么編譯文件是運(yùn)行單元測(cè)試的好方法。(關(guān)于Ant的介紹,請(qǐng)參考我的文章《Ant簡介》(Starting with Ant),發(fā)表于Oracle雜志2002年11/12月號(hào)中)。 假設(shè)你的源文件在src目錄中,所生成的類在tmp目錄中,并且junit.jar庫位于工程的libdirectory目錄中,那么你可以編譯Java源文件,并使用清單3中所示的編譯文件(在工程的根目錄中)運(yùn)行單元測(cè)試。 這個(gè)編譯文件的核心是運(yùn)行單元測(cè)試的測(cè)試目標(biāo)。運(yùn)行這些測(cè)試是這個(gè)目標(biāo)junit的唯一任務(wù)。為了運(yùn)行這一可選任務(wù),必須首先將junit.jar庫放到Ant安裝目錄下的lib目錄中,然后下載并安裝同一目錄中的Ant可選任務(wù)庫。清單3中的示例嵌套了一個(gè)classpath類,它包括JUnit庫和工程的類;示例中還嵌套了一個(gè)batchtest元素,它利用一個(gè)選擇適當(dāng)源文件的fileset元素定義了將要運(yùn)行的測(cè)試。這個(gè)任務(wù)還包括haltonfilure和haltonerror屬性,它們告訴Ant在遇到一個(gè)失敗或錯(cuò)誤時(shí)是否應(yīng)當(dāng)停止。如果將它們的值設(shè)置為"真",那么Ant在遇到第一個(gè)失敗或錯(cuò)誤時(shí)將會(huì)停止,編譯將會(huì)失敗(顯然,這表示在運(yùn)行測(cè)試過程中存在有問題)。另一方面,如果將它們的值設(shè)置為"假",其結(jié)果就不是非常明確了(即使測(cè)試失敗,編譯也會(huì)成功),但所有測(cè)試仍將運(yùn)行。printsummary屬性指示Ant是否顯示運(yùn)行測(cè)試的輸出。數(shù)值withOutAndErr可以在開發(fā)測(cè)試時(shí)方便地告訴Ant顯示標(biāo)準(zhǔn)輸出和錯(cuò)誤輸出。數(shù)值off表示不顯示任何內(nèi)容,而on只顯示測(cè)試報(bào)告(沒有測(cè)試類的輸出)。junit任務(wù)具有很多屬性,詳細(xì)內(nèi)容請(qǐng)參考Ant的文檔。 為了測(cè)試這一編譯文件,你需要建立名字為src、tmp和lib的目錄。將junit.jar庫放到lib目錄中,并將前面看到的示例Java源文件放到src目錄中。打開終端,進(jìn)入該工程的根目錄,并輸入ant,其結(jié)果為: Ant還可以生成非常有用的HTML格式的測(cè)試報(bào)告。為了生成這樣的報(bào)告,將前面的測(cè)試目標(biāo)用以下目標(biāo)代替: 這一目標(biāo)與前面的目標(biāo)相同,只是該目標(biāo)在batchtext元素中增加了一個(gè)新的屬性--todir,它告訴Ant在tmp目錄中生成可擴(kuò)展的標(biāo)記語言(XML)報(bào)告。該目標(biāo)還增加了一個(gè)新的junitreport元素,以便由XML文件生成一個(gè)HTML報(bào)告。這一元素要求在安裝Ant的lib目錄中安裝Xalan庫(詳細(xì)內(nèi)容見Ant文檔的junitreport部分:ant.apache.org/manual/install.html)。這一元素還定義了使用todir屬性生成的文件的目標(biāo)目錄。通過嵌套一個(gè)fileset元素來定義為生成這一報(bào)告而需要處理的XML文件。期望的輸出格式利用嵌套的報(bào)告元素來實(shí)現(xiàn)。該對(duì)象將生成一個(gè)諸如圖4所示的報(bào)告。 這類報(bào)告在使單元測(cè)試自動(dòng)運(yùn)行時(shí)特別有用(比如在夜間編譯期間)。在這些情況下,錯(cuò)誤或失敗不會(huì)中斷測(cè)試,因此你必須將前面提到的junit任務(wù)的haltonfailure和haltonerror屬性設(shè)置為"假"。這一報(bào)告對(duì)于衡量實(shí)施進(jìn)程也非常有用(比如當(dāng)你必須重寫已有代碼時(shí),或者在實(shí)施之前已經(jīng)編寫了測(cè)試的情況下)。 Ant對(duì)啟動(dòng)JUnit圖形運(yùn)行器也非常有用。下面的對(duì)象將會(huì)啟動(dòng)Swing測(cè)試運(yùn)行器: 你應(yīng)當(dāng)在終端中運(yùn)行這一對(duì)象,并且在另一個(gè)終端或你喜歡的IDE中使用Ant對(duì)其進(jìn)行編譯。這種方式使你不必在每次想要測(cè)試代碼時(shí)都啟動(dòng)圖形運(yùn)行器。 在Oracle9i Jdeveloper中的JUnit集成 Oracle9i JDeveloper并沒有基于網(wǎng)絡(luò)集成JUnit,但是下載并安裝這一插件只需要幾分鐘的時(shí)間。為了完成此過程,選擇JDeveloper的"Help"菜單下的"Check for Updates"項(xiàng)。這樣將會(huì)打開IDE更新向?qū)В赃B接到Oracle技術(shù)網(wǎng)站,下載該插件并安裝它。當(dāng)安裝該插件后,需要關(guān)閉并重啟Oracle9i JDeveloper。注意,向?qū)н€會(huì)下載相關(guān)的文檔。 通過為每個(gè)任務(wù)提供向?qū)В@個(gè)插件極大地提高了開發(fā)人員編寫測(cè)試實(shí)例、測(cè)試包和fixture等的工作效率。要調(diào)用這些向?qū)Вc(diǎn)擊"File"菜單下的"New"項(xiàng),然后選擇"General/Unit Tests"類,并從右側(cè)的窗體中選擇合適的向?qū)АD阋部梢詮慕缑嫔蠁?dòng)測(cè)試套件。 當(dāng)準(zhǔn)備好對(duì)項(xiàng)目進(jìn)行代碼測(cè)試后,應(yīng)當(dāng)首先使用專用向?qū)砭帉慺ixture,然后測(cè)試實(shí)例向?qū)Э梢岳盟鼈兗傻綔y(cè)試實(shí)例中。另外,還有一些用來生成自定義測(cè)試fixture的向?qū)б约吧缮虅?wù)組件和數(shù)據(jù)庫連接測(cè)試fixture的向?qū)А_@后兩種向?qū)蓪S么a,以使用setUp()和tearDown()方法設(shè)置和發(fā)布商務(wù)組件或數(shù)據(jù)庫連接。 當(dāng)完成fixture后,下一步應(yīng)當(dāng)使用合適的向?qū)砩蓽y(cè)試實(shí)例,這些向?qū)Э梢宰屇氵x擇要測(cè)試的類和方法。你還可以選擇在這個(gè)測(cè)試中使用的fixture。這將生成一個(gè)使用測(cè)試方法的主體完成的代碼框架。最后應(yīng)當(dāng)生成套件來運(yùn)行你的測(cè)試。這個(gè)專用向?qū)ё屇氵x擇要包括在套件中的測(cè)試,并為你生成整個(gè)類。要啟動(dòng)一個(gè)測(cè)試套件,點(diǎn)擊瀏覽器中的文件,并選擇Run。這將會(huì)啟動(dòng)圖形界面并運(yùn)行套件的測(cè)試。 在"Help"菜單中選擇"Help Topics",你將會(huì)在JDeveloper文檔中找到關(guān)于如何使用這些向?qū)У脑敿?xì)教程。這會(huì)打開幫助系統(tǒng)窗口。點(diǎn)擊"Unit Testing with JUnit"項(xiàng),然后選擇合適的教程。 JUnit和JDeveloper之間的這種集成使你能夠只編寫單元測(cè)試中你感興趣的那部分的代碼,而讓工具為你編寫重復(fù)的代碼。
訪問JUnit網(wǎng)站 下載 Oracle9i應(yīng)用服務(wù)器 學(xué)習(xí)Oracle9i JDeveloper擴(kuò)展 閱讀Oracle9i JDeveloper文檔 JUnit最佳實(shí)踐 下面是一些在使用JUnit時(shí)應(yīng)當(dāng)注意的技巧:
值得花費(fèi)的時(shí)間 到現(xiàn)在,你應(yīng)當(dāng)已經(jīng)清楚地知道使用JUnit框架和合適的工具實(shí)施單元測(cè)試是多么快速而簡單。關(guān)于單元測(cè)試的下一個(gè)目標(biāo)是使你的CTO相信你在實(shí)施測(cè)試時(shí)所必須花費(fèi)的時(shí)間是為了以后節(jié)省更多的時(shí)間。但是,當(dāng)你考慮在檢查老代碼、修正錯(cuò)誤和發(fā)布一個(gè)調(diào)試過的版本上所花費(fèi)的時(shí)間(它可能花費(fèi)整個(gè)一天)時(shí),在開發(fā)過程的早期階段捕獲的代碼錯(cuò)誤毫無疑問是一項(xiàng)很好的投資。這里并沒有考慮當(dāng)錯(cuò)誤代碼不再位于塊的頂部時(shí)開發(fā)人員必須遵循的"black magic"步驟,這些步驟包括:標(biāo)記代碼,制作一個(gè)分支、修正代碼錯(cuò)誤、進(jìn)行發(fā)布,以及將代碼修正合并到塊中。所有這些步驟都非常耗時(shí),并且容易產(chǎn)生錯(cuò)誤。 要開始使用單元測(cè)試和JUnit,請(qǐng)?jiān)L問JUnit網(wǎng)站: www.junit.org。你將找到大量有用的文檔(包括使用JUnit實(shí)施測(cè)試的詳細(xì)說明書)、一個(gè)與JUnit集成的IDE列表,以及關(guān)于JUnit擴(kuò)展工具的詳細(xì)內(nèi)容。 Michel Casabianca ( casa@sweetohm.net)是In-Fusio(一家為移動(dòng)用戶提供游戲服務(wù)的法國公司)的一名軟件工程師,同時(shí)也是XML Pocket Reference(O'Reilly出版,2001年)一書的合著者。
$ ant
Buildfile: build.xml
clean:
[delete] Deleting directory
/Users/casa/doc/oracle
/junit/prj/tmp
[mkdir] Created dir: /Users/casa
/doc/oracle/junit/prj/tmp
bin:
[javac] Compiling 4 source files
to /Users/casa/doc/oracle
/junit/prj/tmp
test:
[junit] Running IsoDateTest
[junit] Tests run: 1, Failures:
0, Errors: 0, Time elapsed:
0,005 sec
[junit] Running IsoDateTest2
[junit] Tests run: 2, Failures: 0,
Errors: 0, Time elapsed: 0,031 sec
[junit] Output:
[junit] setUp()
[junit] testIsoDate()
[junit] tearDown()
[junit] setUp()
[junit] testToString()
[junit] tearDown()
all:
BUILD SUCCESSFUL
Total time: 8 seconds
<target name="test" depends="bin"
description="Run JUnit tests">
<junit haltonfailure="false"
printsummary="withOutAndErr">
<classpath refid="cp"/>
<batchtest todir="${tmp}">
<fileset dir="${src}"
includes="**/*Test*.java"/>
</batchtest>
<formatter type="xml"/>
</junit>
<junitreport todir="${tmp}">
<fileset dir="${tmp}"
includes="TEST-*.xml"/>
<report format="frames"
todir="${tmp}"/>
</junitreport>
</target>
<target name="testui" depends="bin"
description="Run graphical JUnit">
<java classname="junit.swingui.TestRunner"
classpathref="cp"
fork="true"/>
</target>
www.junit.org
Oracle9i Jdeveloper
otn.oracle.com/software/products/jdev/
otn.oracle.com/software/products/ias/
otn.oracle.com/products/jdev/htdocs/partners/addins
otn.oracle.com/docs/products/jdev/
assertEquals(期望原型,實(shí)際原型)
檢查兩個(gè)原型是否相等
assertEquals(期望對(duì)象,實(shí)際對(duì)象)
利用對(duì)象的equals()方法檢查兩個(gè)對(duì)象是否相等
assertSame(期望對(duì)象,實(shí)際對(duì)象)
檢查具有相同內(nèi)存地址的兩個(gè)對(duì)象是否相等
assertNotSame(期望對(duì)象,實(shí)際對(duì)象)
檢查具有不同內(nèi)存地址的兩個(gè)對(duì)象是否不相等
assertNull(對(duì)象 對(duì)象)
檢查一個(gè)對(duì)象是否為空
assertNotNull(對(duì)象 對(duì)象)
檢查一個(gè)對(duì)象是否為非空
assertTrue(布爾條件)
檢查條件是否為真
assertFalse(布爾條件)
檢查條件是否為假
posted on 2005-01-21 16:35 輕松 閱讀(1005) 評(píng)論(0) 編輯 收藏 所屬分類: Junit相關(guān)