1.簡(jiǎn)介
在一篇早些的文章(請(qǐng)參見(jiàn)Test Infected: Programmers Love Writing Tests, Java Report, July 1998, Volume 3, Number 7)中,我們描述了如何使用一個(gè)簡(jiǎn)單的框架來(lái)編寫(xiě)可重復(fù)的測(cè)試。
在本文中我們將匆匆一瞥其內(nèi)中細(xì)節(jié),并向你展示該框架本身是如何被構(gòu)造的。
我們細(xì)致地研究JUint框架并思索如何來(lái)構(gòu)造它。我們發(fā)現(xiàn)了許多不同層次上的教訓(xùn)。在本文中,我們將嘗試著立刻與它們進(jìn)行溝通,這是一個(gè)令人絕望的任務(wù),但至少它是在我們向你展示設(shè)計(jì)和構(gòu)造一件價(jià)值被證實(shí)的軟件的上下文中來(lái)進(jìn)行的。
我們引發(fā)了一個(gè)關(guān)于框架目標(biāo)的討論。在對(duì)框架本身的表達(dá)期間,目標(biāo)將重復(fù)出現(xiàn)許多小的細(xì)節(jié)中。此后,我們提出框架的設(shè)計(jì)和實(shí)現(xiàn)。設(shè)計(jì)將從模式(驚奇,驚奇)的角度進(jìn)行描述,并作為優(yōu)美的程序來(lái)予以實(shí)現(xiàn)。我們總結(jié)了一些的關(guān)于框架開(kāi)發(fā)的想法。
2.什么是JUnit的目標(biāo)呢?
首先,我們不得不回到開(kāi)發(fā)的假定上去。如果缺少一個(gè)程序特性的自動(dòng)測(cè)試(automated test),我們便假定其無(wú)法工作。這看起來(lái)要比主流的假定更加安全,主流的假定認(rèn)為如果開(kāi)發(fā)者向我們保證一個(gè)程序特性能夠工作,那么現(xiàn)在和將來(lái)其都會(huì)永遠(yuǎn)工作。
從這個(gè)觀點(diǎn)來(lái)看,當(dāng)開(kāi)發(fā)者編寫(xiě)和調(diào)試代碼時(shí),它們的工作并沒(méi)有完成,它們還要必須編寫(xiě)測(cè)試來(lái)演示程序能夠工作。然而,每個(gè)人都太忙,他們要做的事情太多,他們沒(méi)有充足的時(shí)間用于測(cè)試。我已經(jīng)有太多的代碼需要編寫(xiě),要我如何再來(lái)編寫(xiě)測(cè)試代碼?回答我,強(qiáng)硬的項(xiàng)目經(jīng)理先生。因此,首要目標(biāo)是編寫(xiě)一個(gè)框架,在這個(gè)框架中開(kāi)發(fā)者能夠看到實(shí)際來(lái)編寫(xiě)測(cè)試的希望之光。該框架必須要使用常見(jiàn)的工具,從而學(xué)習(xí)起來(lái)不會(huì)有太多的新東西。其不能比完全編寫(xiě)一個(gè)新測(cè)試所必須的工作更多。必須排除重復(fù)性的工作。
如果所有測(cè)試都這樣去做的話,你將可以僅在一個(gè)調(diào)試器中編寫(xiě)表達(dá)式來(lái)完成。然而,這對(duì)于測(cè)試而言尚不充分。告訴我你的程序現(xiàn)在能夠工作,對(duì)我而言并沒(méi)有什么幫助,因?yàn)樗](méi)有向我保證你的程序從我現(xiàn)在集成之后的每一分鐘都將會(huì)工作,以及它并沒(méi)有向我保證你的程序?qū)⒁廊荒軌蚬ぷ魑迥辏菚r(shí)你已經(jīng)離開(kāi)了很長(zhǎng)的時(shí)間。
于是,測(cè)試的第二個(gè)目標(biāo)是生成可持續(xù)保持其價(jià)值的測(cè)試。除原作者以外的其他人必須能夠執(zhí)行測(cè)試并解釋其結(jié)果。應(yīng)該能夠?qū)⒉煌髡叩臏y(cè)試結(jié)合起來(lái)并在一起運(yùn)行,而不必?fù)?dān)心相互沖突。
后,必須能夠以現(xiàn)有的測(cè)試作為支點(diǎn)來(lái)生成新的測(cè)試。生成一個(gè)裝置(setup)或夾具(fixture)是昂貴的,并且一個(gè)框架必須能夠?qū)A具進(jìn)行重用,以運(yùn)行不同的測(cè)試。哦,還有別的嗎?
3.JUnit的設(shè)計(jì)
JUnit的設(shè)計(jì)將以一種首次在Patterns Generate Architectures(請(qǐng)參見(jiàn)"Patterns Generate Architectures", Kent Beck and Ralph Johnson, ECOOP 94)中使用的風(fēng)格來(lái)呈現(xiàn)。其思想是通過(guò)從零開(kāi)始來(lái)應(yīng)用模式,然后一個(gè)接一個(gè),直至你獲得系統(tǒng)架構(gòu)的方式來(lái)講解一個(gè)系統(tǒng)的設(shè)計(jì)。我們將提出需要解決的架構(gòu)問(wèn)題,總結(jié)用來(lái)解決問(wèn)題的模式,然后展示如何將模式應(yīng)用于JUnit。
3.1 由此開(kāi)始-TestCase
首先我們必須構(gòu)建一個(gè)對(duì)象來(lái)表達(dá)我們的基本概念,TestCase(測(cè)試案例)。開(kāi)發(fā)者經(jīng)常在頭腦中存在著測(cè)試案例,但在實(shí)現(xiàn)它們的時(shí)候卻采用了許多不同的方式-
· 打印語(yǔ)句
· 調(diào)試器表達(dá)式
· 測(cè)試腳本
如果我們想要輕松地操縱測(cè)試,必須將它們構(gòu)建成對(duì)象。這將會(huì)獲取到一個(gè)僅僅是隱藏在開(kāi)發(fā)者頭腦中的測(cè)試,并使之具體化,其支持我們創(chuàng)建測(cè)試的目標(biāo),即能夠持續(xù)地保持它們的價(jià)值。同時(shí),對(duì)象的開(kāi)發(fā)者比較習(xí)慣于使用對(duì)象來(lái)進(jìn)行開(kāi)發(fā),因此將測(cè)試構(gòu)建成對(duì)象的決定支持我們的目標(biāo)-使測(cè)試的編寫(xiě)更加吸引人(或至少是不太華麗)。
Command(命令)模式(請(qǐng)參見(jiàn)Gamma, E., et al. Design Patterns: Elements of Reusable Object-Oriented Software, Addison-Wesley, Reading, MA, 1995)則能夠比較好地滿足我們的需求。摘引其意圖(intent),“將一個(gè)請(qǐng)求封裝成一個(gè)對(duì)象,從而使你可用不同的請(qǐng)求對(duì)客戶進(jìn)行參數(shù)化;對(duì)請(qǐng)求進(jìn)行排隊(duì)或記錄請(qǐng)求日志...”Command告訴我們可以為一個(gè)操作生成一個(gè)對(duì)象并給出它的一個(gè)“execute(執(zhí)行)”方法。以下代碼定義了TestCase類:
clearcase/" target="_blank" >cccccc width="90%" align=center bgColor=#e1e1e1 border=1>
public abstract class TestCase implements Test {
…
}
因?yàn)槲覀兤谕梢酝ㄟ^(guò)繼承來(lái)對(duì)該類進(jìn)行重用,我們將其聲明為“public abstract”。暫時(shí)忽略其實(shí)現(xiàn)接口Test的事實(shí)。鑒于當(dāng)前設(shè)計(jì)的需要,你可以將TestCase看作是一個(gè)孤立的類。
每一個(gè)TestCase在創(chuàng)建時(shí)都要有一個(gè)名稱,因此若一個(gè)測(cè)試失敗了,你便可識(shí)別出失敗的是哪個(gè)測(cè)試。
public abstract class TestCase implements Test {
private final String fName;
public TestCase(String name) {
fName= name;
}
public abstract void run();
…
}
為了闡述JUnit的演變過(guò)程,我們將使用圖(diagram)來(lái)展示構(gòu)架的快照(snapshot)。我們使用的標(biāo)記很簡(jiǎn)單。其使用包含相關(guān)模式的尖方框來(lái)標(biāo)注類。當(dāng)類在模式中的角色(role)顯而易見(jiàn)時(shí),則僅顯示模式的名稱。如果角色并不清晰,則在尖方框中增加與該類相關(guān)的參與者的名稱。該標(biāo)記可使圖的混亂度降到小限度,并首次見(jiàn)諸于Applying Design Patterns in Java(請(qǐng)參見(jiàn)Gamma, E., Applying Design Patterns in Java, in Java Gems, SIGS Reference Library, 1997)。圖1展示了這種應(yīng)用于TestCase中的標(biāo)記。由于我們是在處理一個(gè)單獨(dú)的類并且沒(méi)有不明確的地方,因此僅顯示模式的名稱。