使用單元測(cè)試來(lái)逐步改進(jìn)代碼
Malcolm Davis 顧問(wèn) 2000 年 11 月
軟件開發(fā)習(xí)慣中一個(gè)細(xì)微更改都可能會(huì)對(duì)軟件質(zhì)量產(chǎn)生巨大改進(jìn)。將單元測(cè)試合并到開發(fā)過(guò)程中,然后從長(zhǎng)遠(yuǎn)角度來(lái)看它可以節(jié)省多少時(shí)間和精力。本文通過(guò)使用代碼樣本說(shuō)明了單元測(cè)試的種種好處,特別是使用 Ant 和 JUnit 帶來(lái)的各種方便。
測(cè)試是大型開發(fā)過(guò)程中的基本原則之一。在任何職業(yè)中,驗(yàn)證都是一個(gè)重要部分。醫(yī)生要通過(guò)驗(yàn)血來(lái)確診。波音公司在研制 777 的過(guò)程中對(duì)飛機(jī)的每個(gè)組件都進(jìn)行了精心測(cè)試。為什么軟件開發(fā)就應(yīng)該例外呢?
以前,由于在應(yīng)用程序中將 GUI 和商業(yè)邏輯緊密聯(lián)系在一起,這就限制了創(chuàng)建自動(dòng)測(cè)試的能力。當(dāng)我們學(xué)會(huì)通過(guò)抽象層將商業(yè)邏輯從界面中分離出來(lái)時(shí),各個(gè)單獨(dú)代碼模塊的自動(dòng)測(cè)試就替代了通過(guò) GUI 進(jìn)行的手工測(cè)試。
現(xiàn)在,集成開發(fā)環(huán)境 (IDE) 能在您輸入代碼的同時(shí)顯示錯(cuò)誤,對(duì)于在類中快速查找方法具有智能探測(cè)功能,可以利用語(yǔ)法結(jié)構(gòu)生成彩色代碼,而且具有許多其它功能。因此,在編譯更改過(guò)的代碼之前,您已經(jīng)全盤考慮了將構(gòu)建的類,但您是否考慮過(guò)這樣的修改會(huì)破壞某些功能呢?
每個(gè)開發(fā)者都碰到過(guò)更改“臭蟲”。代碼修改過(guò)程可能會(huì)引入“臭蟲”,而如果通過(guò)用戶界面手工測(cè)試代碼的話,在編譯完成之前是不會(huì)發(fā)現(xiàn)它的。然后,您就要花費(fèi)幾天的時(shí)間追蹤由更改所引起的錯(cuò)誤。最近在我做的一個(gè)項(xiàng)目中,當(dāng)我把后端數(shù)據(jù)庫(kù)由 Informix 更改到 Oracle 時(shí)就遇到了這種情況。大部分更改都十分順利,但由于數(shù)據(jù)庫(kù)層或使用數(shù)據(jù)庫(kù)層的系統(tǒng)缺少單元測(cè)試,從而導(dǎo)致將大量時(shí)間花費(fèi)在嘗試解決更改“臭蟲”上。我花了兩天的時(shí)間查到別人代碼中的一個(gè)數(shù)據(jù)庫(kù)語(yǔ)法更改。(當(dāng)然,那個(gè)人仍是我的朋友。)
盡管測(cè)試有許多好處,但一般的程序員對(duì)測(cè)試都不太感興趣,開始時(shí)我也沒(méi)有。您聽(tīng)到過(guò)多少次“它編譯了,所以它一定能用”這種言論?但“我思,故我在”這種原則并 不適用于高質(zhì)量軟件。要鼓勵(lì)程序員測(cè)試他們的代碼,過(guò)程必須簡(jiǎn)單無(wú)痛。
本文從某人學(xué)習(xí)用 Java 語(yǔ)言編程時(shí)所寫的一個(gè)簡(jiǎn)單的類開始。然后,我會(huì)告訴您我是如何為這個(gè)類編寫單元測(cè)試,以及在編寫完它以后又是如何將單元測(cè)試添加到構(gòu)建過(guò)程中的。最后,我們將看到將“臭蟲”引入代碼時(shí)發(fā)生的情況。
從一個(gè)典型類開始 第一個(gè)典型的 Java 程序一般都包含一個(gè)打印 "Hello World" 的 main() 。在清單 1 中,我創(chuàng)建了一個(gè) HelloWorld 對(duì)象的實(shí)例并調(diào)用 sayHello() 方法,該方法會(huì)打印這句習(xí)慣說(shuō)法。 清單 1. 我的第一個(gè) Java 應(yīng)用程序 "Hello world"
/*
* HelloWorld.java
* My first java program
*/
class HelloWorld {
/**
* Print "Hello World"
*/
void sayHello() {
System.out.println("Hello World");
}
/**
* Test
*/
public static void main( String[] args ) {
HelloWorld world = new HelloWorld();
world.sayHello();
}
}
|
main() 方法是我的測(cè)試。哦噢!我將代碼、文檔、測(cè)試和樣本代碼包含在了一個(gè)模塊中。保佑 Java!但隨著程序越變?cè)酱?,這種開發(fā)方法很快就開始顯現(xiàn)出了缺陷:
- 混亂
類接口越大, main() 就越大。類可能僅僅因?yàn)檎5臏y(cè)試而變得非常龐大。
- 代碼膨脹
由于加入了測(cè)試,所以產(chǎn)品代碼比所需要的要大。但我不想交付測(cè)試,而只想交付產(chǎn)品。
- 測(cè)試不可靠
既然 main() 是代碼的一部分, main() 就對(duì)其他開發(fā)者通過(guò)類接口無(wú)法訪問(wèn)的私有成員和方法享有訪問(wèn)權(quán)。出于這個(gè)原因,這種測(cè)試方法很容易出錯(cuò)。
- 很難自動(dòng)測(cè)試
要進(jìn)行自動(dòng)測(cè)試,我仍然必須創(chuàng)建另一程序來(lái)將參數(shù)傳遞給 main() 。
類開發(fā) 對(duì)我來(lái)說(shuō),類開發(fā)是從編寫 main() 方法開始的。我在編寫 main() 的時(shí)候就定義類和類的用法,然后實(shí)現(xiàn)接口。它的一些明顯的缺陷也開始顯現(xiàn)出來(lái)。一個(gè)缺陷是我傳遞給 main() 來(lái)執(zhí)行測(cè)試的參數(shù)個(gè)數(shù)。其次, main() 本身在進(jìn)行調(diào)用子方法、設(shè)置代碼等操作時(shí)變得很混亂。有時(shí) main() 會(huì)比類實(shí)現(xiàn)的其余部分還要大。
更簡(jiǎn)單的過(guò)程 我原來(lái)的做法有一些很明顯的缺陷。因此,讓我們看看有什么別的方法可以使問(wèn)題簡(jiǎn)化。我仍然通過(guò)接口設(shè)計(jì)代碼并給出應(yīng)用示例,正如原來(lái)的 main() 一樣。不同的是我將代碼放到了另一個(gè)單獨(dú)的類中,而這個(gè)類恰好是我的“單元測(cè)試”。這種技術(shù)有以下幾點(diǎn)好處:
- 設(shè)計(jì)類的一種機(jī)制
因?yàn)槭峭ㄟ^(guò)接口進(jìn)行開發(fā),所以不太可能利用類的內(nèi)部功能。但因?yàn)槲沂悄繕?biāo)類的開發(fā)者,我有到其內(nèi)部工作的“窗口”,所以測(cè)試并不是個(gè)真正的黑箱。僅憑這一點(diǎn)就足夠推斷出需要開發(fā)者本人在編寫目標(biāo)類的同時(shí)負(fù)責(zé)測(cè)試的開發(fā),而不是由其他任何人代勞。
- 類用法的示例
通過(guò)將示例從實(shí)現(xiàn)中分離出來(lái),開發(fā)者可以更快地提高速度,而且再不用在源代碼上糾纏不清。這種分離還有助于防止開發(fā)者利用類的內(nèi)部功能,因?yàn)檫@些功能將來(lái)可能已經(jīng)不存在了。
- 沒(méi)有類混亂的
main() 我不再受到 main() 的限制了。以前我得將多個(gè)參數(shù)傳遞給 main() 來(lái)測(cè)試不同的配置?,F(xiàn)在我可以創(chuàng)建許多單獨(dú)的測(cè)試類,每一個(gè)都維護(hù)各自的設(shè)置代碼。
接下來(lái)我們將這個(gè)單獨(dú)的單元測(cè)試對(duì)象放入構(gòu)建過(guò)程中。這樣,我們就可以提供自動(dòng)確認(rèn)過(guò)程的方法。
- 確保所做的任何更改都不會(huì)對(duì)其他人產(chǎn)生不利影響。
- 我們?cè)谶M(jìn)行源碼控制之前就可以測(cè)試代碼,而無(wú)需等待匯編測(cè)試或在夜晚進(jìn)行的構(gòu)建測(cè)試。這有助于盡早捕捉到“臭蟲”,從而降低產(chǎn)生高質(zhì)量代碼的成本。
- 通過(guò)提供增量測(cè)試過(guò)程,我們提供了更好的實(shí)現(xiàn)過(guò)程。如同 IDE 幫助我們?cè)谳斎霑r(shí)捕捉到語(yǔ)法或編譯“臭蟲”一樣,增量單元測(cè)試也幫助我們?cè)跇?gòu)建時(shí)捕捉到代碼更改“臭蟲”。
使用 JUnit 自動(dòng)化單元測(cè)試 要使測(cè)試自動(dòng)化,您需要一個(gè)測(cè)試框架。您可以自己開發(fā)或購(gòu)買,也可以使用某些開放源代碼工具,例如 JUnit。我選擇 JUnit 出于以下幾個(gè)原因:
- 不需要編寫自己的框架。
- 它是開放源代碼,因此不需要購(gòu)買框架。
- 開放源代碼社區(qū)中的其他開發(fā)者會(huì)使用它,因此可以找到許多示例。
- 它可以讓我將測(cè)試代碼與產(chǎn)品代碼分開。
- 它易于集成到我的構(gòu)建過(guò)程中。
測(cè)試布局 圖 1 顯示了使用樣本 TestSuite 的 JUnit TestSuite 布局。每個(gè)測(cè)試都由若干單獨(dú)的測(cè)試案例構(gòu)成。每個(gè)測(cè)試案例都是一個(gè)單獨(dú)的類,它擴(kuò)展了 TestClass 類并包含了我的測(cè)試代碼,即那些曾在 main() 中出現(xiàn)的代碼。在該例中,我向 TestSuite 添加了兩個(gè)測(cè)試:一個(gè)是 SkeletonTest,我將它用作所有新類和 HelloWorld 類的起點(diǎn)。
圖 1. TestSuite 布局
測(cè)試類 HelloWorldTest.java 按照約定,測(cè)試類的名稱中包含我所測(cè)試的類的名稱,但將 Test 附加到結(jié)尾。在本例中,我們的測(cè)試類是 HelloWorldTest.java 。我復(fù)制了 SkeletonTest 中的代碼,并添加了 testSayHello() 來(lái)測(cè)試 sayHello() 。請(qǐng)注意 HelloWorldTest 擴(kuò)展了 TestCase。JUnit 框架提供了 assert 和 assertEquals 方法,我們可以使用這些方法來(lái)進(jìn)行驗(yàn)證。 HelloWorldTest.java 顯示在清單 2 中。 清單 2. HelloWorldTest.java
package test.com.company;
import com.company.HelloWorld;
import junit.framework.TestCase;
import junit.framework.AssertionFailedError;
/**
* JUnit 3.2 testcases for HelloWorld
*/
public class HelloWorldTest extends TestCase {
public HelloWorldTest(String name) {
super(name);
}
public static void main(String args[]) {
junit.textui.TestRunner.run(HelloWorldTest.class);
}
public void testSayHello() {
HelloWorld world = new HelloWorld();
assert( world!=null );
assertEquals("Hello World", world.sayHello() );
}
}
|
testSayHello() 看上去和 HelloWorld.java 中原來(lái)的 main 方法類似,但有一個(gè)主要的不同之處。它不是執(zhí)行 System.out.println 并顯示結(jié)果,而是添加了一個(gè) assertEquals() 方法。如果兩個(gè)值不同, assertEquals 將打印出兩個(gè)輸入的值。您可能已經(jīng)注意到這個(gè)方法不起作用!HelloWorld 中的 sayHello() 方法不返回字符串。如果我先寫過(guò)測(cè)試,就會(huì)捕捉到這一點(diǎn)。我將 "Hello World" 字符串與輸出流聯(lián)結(jié)起來(lái)。這樣,按照清單 3 中顯示的那樣重寫了 HelloWorld,去掉 main() ,并更改了 sayHello() 的返回類型。 清單 3. Hello world 測(cè)試案例。
package com.company;
public class HelloWorld {
public String sayHello() {
return "Hello World";
}
}
|
如果我保留了 main() 并修改了聯(lián)系,代碼看上去如下:
public static void main( String[] args ) {
HelloWorld world = new HelloWorld();
System.out.println(world.sayHello());
}
|
新的 main() 與我測(cè)試程序中的 testSayHello() 非常相似。是的,它看上去不象是一個(gè)現(xiàn)實(shí)世界中的問(wèn)題(這是人為示例的問(wèn)題),但它說(shuō)明了問(wèn)題。在單獨(dú)的應(yīng)用程序中編寫 main() 可以改進(jìn)您的設(shè)計(jì),同時(shí)幫助您設(shè)計(jì)測(cè)試?,F(xiàn)在我們已經(jīng)創(chuàng)建了一個(gè)測(cè)試類,讓我們使用 Ant 來(lái)將它集成到構(gòu)建中。
使用 Ant 將測(cè)試集成到構(gòu)建中 Jakarta Project 將 Ant 工具說(shuō)成“不帶 make 缺點(diǎn)的 make”。Ant 正在成為開放源代碼世界中實(shí)際上的標(biāo)準(zhǔn)。原因很簡(jiǎn)單:Ant 是使用 Java 語(yǔ)言編寫的,這種語(yǔ)言可以讓構(gòu)建過(guò)程在多種平臺(tái)上使用。這種特性簡(jiǎn)化了在不同 OS 平臺(tái)之間的程序員的合作,而合作是開放源代碼社區(qū)的一種需要。您可以在自己選擇的平臺(tái)上進(jìn)行開發(fā) 和構(gòu)建。Ant 的特性包括:
- 類可擴(kuò)展性 Java 類可用于擴(kuò)展構(gòu)建特性,而不必使用基于 shell 的命令。
- 開放源代碼 因?yàn)?Ant 是開放源代碼,因此類擴(kuò)展示例很充足。我發(fā)現(xiàn)通過(guò)示例來(lái)學(xué)習(xí)非常棒。
- XML 可配置 Ant 不僅是基于 Java 的,它還使用 XML 文件配置構(gòu)建過(guò)程。假設(shè)構(gòu)建實(shí)際上是分層的,那么使用 XML 描述 make 過(guò)程就是其邏輯層。另外,如果您了解 XML,要學(xué)習(xí)如何配置構(gòu)建就更簡(jiǎn)單一些。
圖 2 簡(jiǎn)要介紹了一個(gè)配置文件。配置文件由目標(biāo)樹構(gòu)成。每個(gè)目標(biāo)都包含了要執(zhí)行的任務(wù),其中任務(wù)就是可以執(zhí)行的代碼。在本例中, mkdir是目標(biāo) compile的任務(wù)。 mkdir是建立在 Ant 中的一個(gè)任務(wù),用于創(chuàng)建目錄。 Ant 帶有一套健全的內(nèi)置任務(wù)。您也可以通過(guò)擴(kuò)展 Ant 任務(wù)類來(lái)添加自己的功能。
每個(gè)目標(biāo)都有唯一的名稱和可選的相關(guān)性。目標(biāo)相關(guān)性需要在執(zhí)行目標(biāo)任務(wù)列表之前執(zhí)行。例如圖 2 所示,在執(zhí)行 compile 目標(biāo)中的任務(wù)之前需要先運(yùn)行 JUNIT 目標(biāo)。這種類型的配置可以讓您在一個(gè)配置中有多個(gè)樹。
圖 2. Ant XML 構(gòu)建圖
與經(jīng)典 make 實(shí)用程序的相似性是非常顯著的。這是理所當(dāng)然的,因?yàn)?make 就是 make。但也要記住有一些差異:通過(guò) Java 實(shí)現(xiàn)的跨平臺(tái)和可擴(kuò)展性,通過(guò) XML 實(shí)現(xiàn)的可配置,還有開放源代碼。
下載和安裝 Ant 首先下載 Ant(請(qǐng)參閱 參考資料 )。將 Ant 解壓縮到 tools 目錄,再將 Ant bin 目錄添加到路徑中。(在我的機(jī)器上是 e:\tools\ant\bin 。)設(shè)置 ANT_HOME 環(huán)境變量。在 NT 中,這意味著進(jìn)入系統(tǒng)屬性,然后以帶有值的變量形式添加 ANT_HOME。ANT_HOME 應(yīng)該設(shè)置為 Ant 根目錄,即包含 bin 和 lib 目錄的目錄。(對(duì)我來(lái)說(shuō),是 e:\tools\ant 。)確保 JAVA_HOME 環(huán)境變量設(shè)置為安裝了 JDK 的目錄。Ant 文檔有關(guān)于安裝的詳細(xì)信息。
下載和安裝 JUnit 下載 JUnit 3.2(請(qǐng)參閱 參考資料 )。解開 junit.zip ,并將 junit.jar 添加到 CLASSPATH。如果將 junit.zip 解包到類路徑中,可以通過(guò)運(yùn)行以下命令來(lái)測(cè)試安裝: java junit.textui.TestRunner junit.samples.AllTests
定義目錄結(jié)構(gòu) 在開始我們的構(gòu)建和測(cè)試過(guò)程之前,需要一個(gè)項(xiàng)目布局。圖 3 顯示了我的樣本項(xiàng)目的布局。下面描述了布局的目錄結(jié)構(gòu):
build -- 類文件的臨時(shí)構(gòu)建位置。構(gòu)建過(guò)程將創(chuàng)建這個(gè)目錄。
src -- 源代碼的位置。 Src 被分為 test 文件夾和 main 文件夾,前者用于所有的測(cè)試代碼,而后者包含可交付的代碼。將測(cè)試代碼與主要代碼分離提供了幾點(diǎn)特性。首先,使主要代碼中的混亂減少。其次,它允許包對(duì)齊。我就熱衷與將類和與其相關(guān)的包放置在一起。測(cè)試就應(yīng)該和測(cè)試在一起。它還有助于分發(fā)過(guò)程,因?yàn)槟悴豢赡艽蛩銓卧獪y(cè)試分發(fā)給客戶。
在實(shí)際中,我們有多個(gè)目錄,例如 distribution 和 documentation 。我們還會(huì)在 main 下有多個(gè)用于包的目錄,例如 com.company.util 。
因?yàn)槟夸浗Y(jié)構(gòu)經(jīng)常變動(dòng),所以在 build.xml 中有這些變動(dòng)的全局字符串常數(shù)是很重要的。
圖 3. 項(xiàng)目布局圖
Ant 構(gòu)建配置文件示例 下一步,我們要?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:\projects\sample>ant runtests
Searching for build.xml ...
Buildfile: E:\projects\sample\build.xml
JUNIT:
compile:
[mkdir] Created dir: E:\projects\sample\build\classes
[javac] Compiling 1 source file to E:\projects\sample\build\classes
jar:
[mkdir] Created dir: E:\projects\sample\build\lib
[jar] Building jar: E:\projects\sample\build\lib\sample.jar
compiletests:
[mkdir] Created dir: E:\projects\sample\build\testcases
[javac] Compiling 3 source files to E:\projects\sample\build\testcases
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:\projects\sample>ant runtests
Searching for build.xml ...
Buildfile: E:\projects\sample\build.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:\projects\sample\build.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ì)量就更好。
參考資料
關(guān)于作者 Malcolm G. Davis 擁有自己的咨詢公司,并任公司的總裁,該公司位于美國(guó)阿拉巴馬州的伯明翰 (Birmingham)。他把自己看做是個(gè) Java 傳道者。在工作之余,他喜歡跑步,以及和他的孩子們一起玩耍。您可以通過(guò) malcolm@nuearth.com 與 Malcolm 聯(lián)系。 |
|