您的位置:軟件測(cè)試 > 開源軟件測(cè)試 > 開源單元測(cè)試工具 >
追求代碼質(zhì)量: 親身體驗(yàn)行為驅(qū)動(dòng)開發(fā)
作者:網(wǎng)絡(luò)轉(zhuǎn)載 發(fā)布時(shí)間:[ 2013/2/25 14:28:41 ] 推薦標(biāo)簽:

        測(cè)試驅(qū)動(dòng)的開發(fā)(TDD)在實(shí)踐中是一個(gè)很好的思想,但有些開發(fā)人員還不能接受 “測(cè)試” 這個(gè)詞所產(chǎn)生的概念上的驟變。在本文中,學(xué)習(xí)一種更自然的方法,將 TDD 元素整合到編程實(shí)踐中。開始采用行為驅(qū)動(dòng)開發(fā)(BDD)(通過 JBehave),親身體驗(yàn)將注意力集中在程序行為(而不是輸出)時(shí)獲得的效果。
        顯然,測(cè)試本身是件好事。而在早期進(jìn)行測(cè)試 — 例如在編寫代碼時(shí) — 則更有益處,這特別有利于提高代碼質(zhì)量。在開發(fā)早期編寫測(cè)試,您將獲益良多。您能夠檢查代碼的行為,并預(yù)先對(duì)它進(jìn)行調(diào)試,這種動(dòng)力無疑是巨大的。

        即使了解了這種重要性,我們也沒有達(dá)到關(guān)鍵的一點(diǎn):使在編寫代碼之前 編寫測(cè)試成為一種標(biāo)準(zhǔn)實(shí)踐。正如 TDD 是極限編程(Extreme Programming)的下一個(gè)演化階段(后者推出了單元測(cè)試框架),以 TDD 為基礎(chǔ),新的飛躍也將到來。本月,我邀請(qǐng)您和我一起實(shí)現(xiàn)從 TDD 到更具直觀性的行為驅(qū)動(dòng)測(cè)試(BDD)的演化。
行為驅(qū)動(dòng)開發(fā)

        雖然測(cè)試優(yōu)先編程對(duì)于有些人比較管用,但是并不適用于每一個(gè)人。雖然有的應(yīng)用程序開發(fā)人員狂熱擁護(hù) TDD,但也有人堅(jiān)決抵制它。即使現(xiàn)在已經(jīng)有了很多測(cè)試框架,例如 TestNG、 Selenium 和 FEST,但不對(duì) 代碼進(jìn)行測(cè)試的理由仍然充分。

        不采用 TDD 的兩個(gè)常見理由是 “沒有足夠的時(shí)間進(jìn)行測(cè)試” 和 “代碼太復(fù)雜,難以測(cè)試”。測(cè)試優(yōu)先編程的另一個(gè)障礙是測(cè)試優(yōu)先概念本身。很多人把測(cè)試看作一種反應(yīng)型活動(dòng),僅比抽象具體一點(diǎn)。經(jīng)驗(yàn)告訴我們,不能測(cè)試不存在的東西。對(duì)于某些開發(fā)人員來說,對(duì)于這種概念框架,測(cè)試優(yōu)先 是一種矛盾的說法。

        但是,如果不考慮編寫測(cè)試和如何測(cè)試,而是考慮行為,結(jié)果會(huì)如何呢?這里所說的行為,是指一個(gè)應(yīng)用程序應(yīng)該 如何運(yùn)行 — 實(shí)際上是指它的規(guī)范。

        實(shí)際上,您已經(jīng)想到了這種方法。我們都想到過。請(qǐng)看下面的對(duì)話。

Frank: 什么是棧?

Linda: 它是一種數(shù)據(jù)結(jié)構(gòu),按先進(jìn)后出(或后進(jìn)先出)的方式收集對(duì)象。它通常有一個(gè) API,其中包括 push() 和 pop() 等方法。有時(shí)也有 peek() 方法。

Frank: push() 有什么功能?

Linda: push() 接受一個(gè)輸入對(duì)象,比如說 foo,并將它放入到一個(gè)內(nèi)部容器(例如一個(gè)數(shù)組)中。push() 通常不返回結(jié)果。

Frank: 如果我 push() 兩個(gè)對(duì)象,比如先是 foo,然后是 bar,結(jié)果會(huì)怎樣?

Linda: 第二個(gè)對(duì)象 bar 應(yīng)該在棧(至少包含兩個(gè)對(duì)象)的頂部,所以如果調(diào)用 pop(),那么返回的應(yīng)該是 bar,而不是 foo。如果再次調(diào)用 pop(),那么應(yīng)該返回 foo,然后棧為空(假設(shè)在添加這兩個(gè)對(duì)象之前棧中沒有對(duì)象)。

Frank: 也是說,pop 移除近放入棧中的項(xiàng)目?

Linda: 是的,pop() 應(yīng)該移除上面的項(xiàng)目(假設(shè)棧中還有可移除的項(xiàng)目)。peek() 與此類似,只是不移除棧中的對(duì)象。peek() 應(yīng)該保留棧頂?shù)捻?xiàng)目。

Frank: 如果之前沒有 push 任何項(xiàng)目,那么調(diào)用 pop() 時(shí)會(huì)怎樣?

Linda: pop() 應(yīng)該拋出一個(gè)異常,表明棧中尚未 push 任何項(xiàng)。

Frank: 如果 push() null 會(huì)怎樣?

Linda: 棧應(yīng)該拋出一個(gè)異常,因?yàn)?null 不是一個(gè)有效的可 push() 的值。
在這段對(duì)話中,有沒有注意到什么特別的地方呢(除了 Frank 不是計(jì)算機(jī)科學(xué)專業(yè)的)?這里從頭到尾沒有用到 “測(cè)試” 這個(gè)詞。但是,“應(yīng)該” 這個(gè)詞卻非常自然地隨處閃現(xiàn)。

怎么做才自然?

        BDD 并不是什么新生事物,更不具備什么革命性的突破。它只是 TDD 的一個(gè)分支,其中 “測(cè)試” 這個(gè)詞換成了 “應(yīng)該”。除了語義,很多人還發(fā)現(xiàn),與測(cè)試 概念相比,應(yīng)該 這個(gè)概念是一種更自然的開發(fā)驅(qū)動(dòng)因素?紤]行為(應(yīng)該)會(huì)自然而然地促使您先編寫規(guī)范類,而后者可以成為一個(gè)非常有效的實(shí)現(xiàn)驅(qū)動(dòng)因素。

        以 Frank 和 Linda 的對(duì)話為基礎(chǔ),讓我們看看 BDD 如何以 TDD 希望推廣的方式驅(qū)動(dòng)開發(fā)。

JBehave

        JBehave 是用于 Java™ 平臺(tái)的一個(gè) BDD 框架,源于 xUnit 范例。正如您所料,JBehave 強(qiáng)調(diào)應(yīng)該 這個(gè)詞,而不是測(cè)試。和 JUnit 一樣,您可以在自己喜歡的 IDE 中,或者通過偏愛的構(gòu)建平臺(tái)(例如 Ant)運(yùn)行 JBehave 類。

        JBehave 允許以 JUnit 的方式創(chuàng)建行為類;但是,在 JBehave 中,不需要擴(kuò)展任何特定的基類,并且所有行為方法都需要以 should 而不是 test 開頭,如清單 1 所示。


清單 1. 用于棧的一個(gè)簡(jiǎn)單的行為類
              
public class StackBehavior {
 public void shouldThrowExceptionUponNullPush() throws Exception{}
 public void shouldThrowExceptionUponPopWithoutPush() throws Exception{}
 public void shouldPopPushedValue() throws Exception{}
 public void shouldPopSecondPushedValueFirst() throws Exception{}
 public void shouldLeaveValueOnStackAfterPeep() throws Exception{}
}
 


        清單 1 中定義的方法都是以應(yīng)該開頭,它們都創(chuàng)建一個(gè)人類可讀的句子。這里產(chǎn)生的 StackBehavior 類描述 Frank 和 Linda 之間的對(duì)話中提到的棧的很多特性。

        例如,Linda 說,如果用戶試圖將 null 放到棧上,那么棧應(yīng)該 拋出一個(gè)異常。查看 StackBehavior 類中的第一個(gè)行為方法:該方法的方法名為 shouldThrowExceptionUponNullPush()。其它方法的命名也遵從這一模式。這種描述性命名模式(這并不是 JBehave 或 BDD 特有的)便于以人類可讀的方式報(bào)告失敗行為,您很快可以看到這一點(diǎn)。

        說到 shouldThrowExceptionUponNullPush(),那么如何驗(yàn)證這個(gè)行為呢?似乎 Stack 類首先需要有一個(gè) push() 方法,這很容易定義。


        清單 2. 用于探索行為的一個(gè)簡(jiǎn)單的棧定義
              
public class Stack<E> {
 public void push(E value) {}
}
 


        可以看到,我編寫了一個(gè)簡(jiǎn)單的棧,以便首先 添加必需的行為。正如 Linda 所說,行為很簡(jiǎn)單:如果有人對(duì) null 值調(diào)用 push(),那么棧應(yīng)該 拋出一個(gè)異!,F(xiàn)在看看我在清單 3 中如何定義這個(gè)行為。


        清單 3. 如果推出一個(gè) null 值,則棧應(yīng)該拋出一個(gè)異常
              
public void shouldThrowExceptionUponNullPush() throws Exception{
 final Stack<String> stStack = new Stack<String>();

 Ensure.throwsException(RuntimeException.class, new Block(){
   public void run() throws Exception {
    stStack.push(null);
   }
 });
}

上一頁1234下一頁
軟件測(cè)試工具 | 聯(lián)系我們 | 投訴建議 | 誠聘英才 | 申請(qǐng)使用列表 | 網(wǎng)站地圖
滬ICP備07036474 2003-2017 版權(quán)所有 上海澤眾軟件科技有限公司 Shanghai ZeZhong Software Co.,Ltd