下一步,我們要?jiǎng)?chuàng)建配置文件。清單 4 顯示了一個(gè) Ant 構(gòu)建文件示例。構(gòu)建文件中的關(guān)鍵是名為 runtests 的目標(biāo)。這個(gè)目標(biāo)進(jìn)行分支判斷并運(yùn)行外部程序,其中外部程序是前面已安裝的 junit.textui.TestRunner 。我們指定要使用語(yǔ)句 test.com.company.AllJUnitTests 來(lái)運(yùn)行哪個(gè)測(cè)試套件。
清單 4. 構(gòu)建文件示例
<property name="app.name" value="sample" /> <property name="build.dir" value="build/classes" /> <target name="JUNIT"> <available property="junit.present" classname="junit.framework.TestCase" /> </target> <target name="compile" depends="JUNIT"> <mkdir dir="${build.dir}"/> <javac srcdir="src/main/" destdir="${build.dir}" > <include name="**/*.java"/> </javac> </target> <target name="jar" depends="compile"> <mkdir dir="build/lib"/> <jar jarfile="build/lib/${app.name}.jar" basedir="${build.dir}" includes="com/**"/> </target> <target name="compiletests" depends="jar"> <mkdir dir="build/testcases"/> <javac srcdir="src/test" destdir="build/testcases"> <classpath> <pathelement location="build/lib/${app.name}.jar" /> <pathelement path="" /> </classpath> <include name="**/*.java"/> </javac> </target> <target name="runtests" depends="compiletests" if="junit.present"> <java fork="yes" classname="junit.textui.TestRunner" taskname="junit" failonerror="true"> <arg value="test.com.company.AllJUnitTests"/> <classpath> <pathelement location="build/lib/${app.name}.jar" /> <pathelement location="build/testcases" /> <pathelement path="" /> <pathelement path="${java.class.path}" /> </classpath> </java> </target> </project>
運(yùn)行 Ant 構(gòu)建示例
開發(fā)過(guò)程中的下一步是運(yùn)行將創(chuàng)建和測(cè)試 HelloWorld 類的構(gòu)建。清單 5 顯示了構(gòu)建的結(jié)果,其中包括了各個(gè)目標(biāo)部分?岬哪遣糠质 runtests 輸出語(yǔ)句:它告訴我們整個(gè)測(cè)試套件都正確運(yùn)行了。
我在圖 4 和圖 5 中顯示了 JUnit GUI,其中所要做的是將 runtest 目標(biāo)從 junit.textui.TestRunner 改為 junit.ui.TestRunner 。當(dāng)您使用 JUnit 的 GUI 部分時(shí),您必須選擇退出按鈕來(lái)繼續(xù)構(gòu)建過(guò)程。如果使用 Junit GUI 構(gòu)建包,那么它將更難與大型的構(gòu)建過(guò)程相集成。另外,文本輸出也與構(gòu)建過(guò)程更一致,并可以定向輸出到一個(gè)用于主構(gòu)建記錄的文本文件。這對(duì)于每天晚上都要進(jìn)行的構(gòu)建非常合適。
清單 5. 構(gòu)建輸出示例
E:projectssample>ant runtests Searching for build.xml ... Buildfile: E:projectssampleuild.xml JUNIT: compile: [mkdir] Created dir: E:projectssampleuildclasses [javac] Compiling 1 source file to E:projectssampleuildclasses jar: [mkdir] Created dir: E:projectssampleuildlib [jar] Building jar: E:projectssampleuildlibsample.jar compiletests: [mkdir] Created dir: E:projectssampleuild estcases [javac] Compiling 3 source files to E:projectssampleuild estcases runtests: [junit] .. [junit] Time: 0.031 [junit] [junit] OK (2 tests) [junit] BUILD SUCCESSFUL Total time: 1 second
圖 4. JUnit GUI 測(cè)試成功
圖 5. JUnit GUI 測(cè)試失敗
了解測(cè)試的工作原理
讓我們搞點(diǎn)破壞,然后看看會(huì)發(fā)生什么事。夜深了,我們決定把 "Hello World" 變成一個(gè)靜態(tài)字符串。在更改期間,我們 不小心 打錯(cuò)了字母,將 "o" 變成了 "0",如清單 6 所示。
清單 6. Hello world 類更改
package com.company; public class HelloWorld { private final static String HELLO_WORLD = "Hell0 World"; public String sayHello() { return HELLO_WORLD; } }
在構(gòu)建包時(shí),我們看到了錯(cuò)誤。清單 7 顯示了 runtest 中的錯(cuò)誤。它顯示了失敗的測(cè)試類和測(cè)試方法,并說(shuō)明了為什么會(huì)失敗。我們返回到代碼中,改正錯(cuò)誤后離開。
清單 7. 構(gòu)建錯(cuò)誤示例
E:projectssample>ant runtests Searching for build.xml ... Buildfile: E:projectssampleuild.xml JUNIT: compile: jar: compiletests: runtests: [junit] ..F [junit] Time: 0 [junit] [junit] FAILURES!!! [junit] Test Results: [junit] Run: 2 Failures: 1 Errors: 0 [junit] There was 1 failure: [junit] 1) testSayHello(test.com.company.HelloWorldTest) "expected:<Hello World> but was:<Hell0 World>" [junit] BUILD FAILED E:projectssampleuild.xml:35: Java returned: -1 Total time: 0 seconds
并非完全無(wú)痛
新的過(guò)程并不是完全無(wú)痛的。為使單元測(cè)試成為開發(fā)的一部分,您必須采取以下幾個(gè)步驟:
下載和安裝 JUnit。
下載和安裝 Ant。
為構(gòu)建創(chuàng)建單獨(dú)的結(jié)構(gòu)。
實(shí)現(xiàn)與主類分開的測(cè)試類。
學(xué)習(xí) Ant 構(gòu)建過(guò)程。
但好處遠(yuǎn)遠(yuǎn)超過(guò)了痛苦。通過(guò)使單元測(cè)試成為開發(fā)過(guò)程的一部分,您可以:
自動(dòng)驗(yàn)證以捕捉更改“臭蟲”
從接口角度設(shè)計(jì)類
提供干凈的示例
在發(fā)行包中避免代碼混亂和類膨脹。
實(shí)現(xiàn) 24x7
保證產(chǎn)品的質(zhì)量要花費(fèi)很多錢,但如果質(zhì)量有缺陷,花費(fèi)的錢更多。如何才能使所花的錢獲得大價(jià)值,來(lái)保證產(chǎn)品質(zhì)量呢?
評(píng)審設(shè)計(jì)和代碼。 評(píng)審可以達(dá)到的效果是單純測(cè)試的一半。
通過(guò)單元測(cè)試來(lái)確認(rèn)模塊可以使用。 盡管測(cè)試早存在,但隨著開發(fā)實(shí)踐的不斷發(fā)展,單元測(cè)試逐漸成為日常開發(fā)過(guò)程的一個(gè)部分。
在我 10 年的開發(fā)生涯里,為 emageon.com 工作是重要的部分之一。在 emageon.com 時(shí),設(shè)計(jì)評(píng)審、代碼評(píng)審和單元測(cè)試是每天都要做的事。這種日常開發(fā)習(xí)慣造了高質(zhì)量的產(chǎn)品。軟件在客戶地點(diǎn)第一年的當(dāng)機(jī)次數(shù)為零,是一個(gè)真正的 24x7 產(chǎn)品。單元測(cè)試象刷牙:您不一定要做,但如果做了,生活質(zhì)量更好。
參考資料
您可以參閱本文在 developerWorks 全球站點(diǎn)上的 英文原 .
下載在本文中引用的 示例代碼 。
從 Apache網(wǎng)站下載 Ant。如需 Ant 文檔、FAQ 和其他下載,請(qǐng)?jiān)L問(wèn) Jakarta 項(xiàng)目的Ant 主頁(yè) 。
JUnit 主頁(yè) 提供了額外的測(cè)試示例、文檔、文章和 FAQ。您可以從 www.xprogramming.com下載 JUnit 3.2。
Kent Beck 所寫的 “簡(jiǎn)單的 Smalltalk 測(cè)試”(Simple Smalltalk Testing) 討論了一個(gè)簡(jiǎn)單的測(cè)試策略和支持它的框架。
請(qǐng)參閱其它開發(fā)者的有關(guān)單元測(cè)試的評(píng)論 (comments on unit testing) 。
要了解其它有用的開發(fā)習(xí)慣,請(qǐng)?jiān)L問(wèn) 編程主頁(yè) (Extreme Programming Home page) 。
關(guān)于作者
Malcolm G. Davis 擁有自己的咨詢公司,并任公司的總裁,該公司位于美國(guó)阿拉巴馬州的伯明翰 (Birmingham)。他把自己看做是個(gè) Java 傳道者。在工作之余,他喜歡跑步,以及和他的孩子們一起玩耍。您可以通過(guò) malcolm@nuearth.com與 Malcolm 聯(lián)系。