也許由于軟件行業(yè)固有的“高科技”特性(或者說,軟件人固有的顧影自憐),軟件項目的管理和過程控制中從來就不缺少形形色色的工具。在一些大型的、“正規(guī)的”軟件企業(yè)中,配置管理要通過ClearCase,軟件設計要使用Rose畫出一大堆精美的圖形,壓力測試要用LoadRunner來跑……當軟件公司的老板們癡迷于“形式化管理”與漂亮的報表和文檔時,這些昂貴的商業(yè)工具著實在他們那里得到了不少的青睞。
幸運的是,越來越多的軟件開發(fā)者和老板開始意識到,軟件項目采用的方法沒有一定之規(guī),不同的項目、不同的團隊需要選擇不同的開發(fā)方法。而工具,則如同三棱鏡般折射出方法學的身影——用著微軟的TeamSystem就很難不遵循微軟推薦的最佳實踐,同樣RUP在沒有Rational工具支持的情況下也難以實施。于是,選擇開發(fā)過程工具,很大程度上就成了選擇開發(fā)方法的一個副產品。
對于身處激烈需求變更風暴之中的企業(yè)應用開發(fā)者,如何步步緊跟客戶的真實需求、如何確保時刻為客戶提供最大價值是他們每天冥思苦想的問題。此時注重交流反饋、以客戶價值為驅動的各種敏捷開發(fā)方法就成為了他們自然而然的選擇。而有趣的是,開源的過程工具也大多與敏捷方法最為適應??此婆既?,其實卻有其道理:開源項目更少受到種種政治因素的影響,生存的環(huán)境又有更多的不確定性,因此也更加強調時刻保證最大化的客戶價值。而這種思路,與敏捷方法是不謀而合的。再加上,推崇敏捷方法的那些“實用主義程序員”們往往也正是開源社群的積極分子,所以適用于敏捷項目的開源過程工具尤其容易找到也就不足為奇了。
敏捷的開發(fā)者們是幸福的,因為他們擁有眾多優(yōu)秀的開源工具可供選擇;敏捷的開發(fā)者們又是痛苦的,因為他們必須在亂花漸欲迷人眼的工具叢中找出適合自己的一組工具棧,并將它們與自己的管理策略糅合成一個完整的開發(fā)過程。本文將為讀者介紹ThoughtWorks公司常用的一組過程工具,以及在敏捷項目中使用這些工具的些許經驗,希望能幫助讀者略微緩解這種痛苦。
作為一個程序員,筆者對過程工具的關注也更多地集中在技術層面上。從我的角度看來,敏捷方法最為重要、也最立竿見影的部分當屬測試驅動與持續(xù)集成,我們的工具之旅就從這兩件事情開始。
在敏捷開發(fā)的工具箱里,JUnit很可能是最廣為人知、也最受到重視的一個,當然它也當得起這樣的殊榮。若論出身,JUnit是由Kent Beck與Erich Gamma兩人共同創(chuàng)造的。若論影響,由它引入的“紅條/綠條”更是影響深遠。
圖1:各種各樣的Green Bars
“紅條/綠條”不僅僅是一個測試成功與失敗的標記,它們構成了敏捷開發(fā)中最基本的韻律:“編寫測試 – 紅條 – 編寫代碼 – 綠條 – 重構”,這五個簡單的步驟確保了每一個思緒都有單元測試作為記錄,每一段代碼都有單元測試保證它的質量,整個軟件項目始終朝著價值最大化、質量最優(yōu)化的方向前進。而紅條與綠條,則清晰直觀地指出你當前所在的位置,以及下一步應該做的工作。一個醒目的紅條/綠條對于開發(fā)者的心理有著如此重要的暗示作用,以至于當微軟在Visual Studio 2005的單元測試工具中沒有放置這樣一個醒目的標志時,竟引來了開發(fā)者的一片怨聲載道。
當我說JUnit的時候,我意指的是整個xUnit家族——覆蓋了從Java/.NET直到C/C++再到Haskell/Eiffel最后到JavaScript/Ruby/Python的大家族。雖然運行在不同平臺、不同語言,它們擁有同一組無法錯認的特征:紅條/綠條、TestCase/TestSuite……這也讓開發(fā)者們無論走到哪里總可以感到放心。在開始編寫你的任何一段代碼之前,先為它寫上一段測試;盡可能頻繁地運行所有的測試。你可以立刻感受到TDD帶來的幫助。
不過還有一個催化劑可以讓這種化學變化來得更加強烈,那就是mock框架。以EasyMock和JMock為代表的mock框架,其作用是模擬被測對象之外的相關對象,從而實現對象之間的解耦合,達到真正意義上的“單元測試”。奇妙的是,當你把mock框架放入測試驅動實踐中時,開發(fā)過程就會引導著你得到低耦合、模塊化的設計,因為你很難為一組緊密耦合的類編寫測試,而mock框架則讓你愿意采用IoC模式分離出更多的類來承擔各自的責任。在.NET和Ruby等主要平臺上,也有類似的mock框架可供選擇。
拋開所有的技術考量,單憑直覺來說,我個人認為Selenium最大的優(yōu)點在于它的震撼性:成百上千個test case運行起來,只見web頁面不斷地在屏幕上閃過,模擬著各種各樣的用戶操作,最后生成一張龐大的報表,背景是令人平靜愉悅的淡綠色。即便對于全然對于不懂技術的管理者或客戶,這一過程的心理效果也是不言而喻的。
圖2:Selenium也采用了紅條/綠條的界面設計
在沒有使用Selenium之前,很多人都認為web界面是無法測試驅動、也無需測試驅動的。但此時我們就常常遇到這樣的困境:客戶認為頁面上的一句話應該這樣寫,開發(fā)者卻認為客戶三天前的意見是另一樣,認真回想起來卻又沒人能記得三天前到底說過什么。如果說JUnit單元測試記錄了開發(fā)者對于軟件的設計思路,那么Selenium就記錄了客戶對于軟件功能的要求,并時時驗證客戶的要求仍然得到滿足。
Selenium的用法有很多種。你可以把它放在構建流程之外,由客戶定期檢驗軟件的功能是否符合需求;你也可以讓它作為構建流程的一部分,用它來驅動功能的開發(fā),形成一個更大范圍的“紅-綠-重構”循環(huán)。除了編寫HTML格式的測試腳本之外,Selenium還支持編程驅動的模式,可以用程序代碼來編寫可復用的測試案例。而且整個Selenium是用JavaScript和HTML編寫的,這也就意味著你可以輕松地將其融入任何一個web應用,不管開發(fā)這個應用的平臺是J2EE還是.NET或者Rails。
Selenium的名字還有一個有趣的由來:在Selenium出現之前,最著名的web應用功能測試工具當屬Mercury Quanlity Center,但那是一個商業(yè)工具,功能強大卻也價格不菲,常常讓開發(fā)者們又愛又恨。所以,自己動手開發(fā)開源功能測試工具的ThoughtWorker們把這個工具叫做Selenium——“mercury”有“水銀”的意思,而“selenium”(硒元素)恰好是專解汞中毒的特效藥。
現在你已經用JUnit記下了所有設計思路,也用Selenium記下了所有功能需求。你還需要讓所有測試案例都能自動運行,這樣才能頻繁地驗證所有測試仍然順利通過。這時Ant就來到了你的手邊。由Apache組織開發(fā),又有多年的實踐檢驗,Ant已經積累了大量實用的插件,幾乎所有常用的任務都有對應的插件可以完成。而且在Ant腳本中可以直接調用Java類(.NET平臺下的NAnt更是可以直接在腳本中插入C#代碼),也就是說你實際上可以用Ant來做任何事。
不過最通常的用法,還是用Ant來完成整個構建流程:從編譯源代碼,到打包應用,到部署服務器,到初始化數據庫,再到執(zhí)行測試并生成測試報告,只需要一個指令就可以全部完成。類似的構建工具在C/C++的世界里早已存在,那就是著名的make,不過Ant來得更加簡單易用而已。
作為后起之秀的Maven比之Ant最大的優(yōu)勢在于它內建了更多對J2EE項目(尤其是web項目)的支持,以及更多項目管理相關的功能。像單元測試、創(chuàng)建報表等在Ant中用插件實現的功能,在Maven中都有內建的支持。甚至很多開源項目的網站都是用Maven直接生成的。但以筆者愚見,Maven強大的功能更適合開源軟件這樣組織松散的項目,對于一般的企業(yè)應用Ant已經足夠了。
不過在進入了Ruby/Rails的世界之后,筆者感覺Ant和Maven都有一個共同的缺陷:它們的構建腳本都采用XML編寫,因此代碼量相當巨大,并且也不易理解。相比之下,Rake直接用Ruby來編寫構建腳本,反倒顯得更加簡潔易用。也許相比于XML,我們還需要找到另一種更適合描述“軟件構建”這一任務的領域專用語言。
任何一個多人開發(fā)的項目都必須有版本控制系統(tǒng)——即便一個人開發(fā),也需要版本控制系統(tǒng)來提供備份和恢復的支持。由于有全面的測試作為保障,敏捷方法提倡采用“所有人擁有所有代碼”的代碼所有權形式,這也就意味著時常會出現兩個人同時修改一份文件的情況,在一些配置文件上這種情況出現得更加頻繁。因此,敏捷開發(fā)所使用的版本控制系統(tǒng)最好不要采用文件級的鎖定——這正是VSS缺省的鎖定方式,不過我們也可以通過配置來改變鎖定方式。而開源的選擇,則是CVS與Subversion(簡稱SVN)。
CVS與SVN存在著一些差異,不過作為開發(fā)者的我們通常不必介意這些區(qū)別。值得注意的一點是,當出現文件沖突時,SVN會強迫用戶首先消解沖突,然后才能將文件提交到代碼庫。另外,每當有人成功提交新文件之后,SVN會為整個項目生成一個新的修訂版本(revision),而不是像CVS那樣為每個文件單獨記錄修訂版本,這也讓版本控制變得更加容易。
在一個敏捷項目中,每個開發(fā)pair每隔15分鐘到半小時(最多不超過1小時)就會提交自己最新的代碼,一個項目組每天通常會生成數十個修訂版本。即便不算其中無法成功構建的版本,通常每周也能收獲上百個構建版本。察看每個構建版本對應的報表,就是項目管理者有效掌握項目進度的最佳途徑。
我們剛才已經提到,在開發(fā)者不斷提交的修訂版本中,有一部分是無法成功構建的,原因就是在版本控制系統(tǒng)上運行著一個忠實的看門人:持續(xù)集成工具。它們的代表就是由ThoughtWorks員工開發(fā)和維護的CruiseControl。
CruiseControl的任務非常簡單:每當有人提交了新的文件到代碼庫,它就把整個項目簽出(check-out)到一個測試環(huán)境,然后執(zhí)行項目的構建腳本,完成整個構建流程,并運行所有測試。如果這一切都順利通過,CruiseControl就會生成一個新的構建版本;否則,構建失敗,所有人都不允許再簽出或提交任何代碼,直到造成破壞失敗的人把問題解決掉,讓構建重新成功為止。
一個能夠成功構建的代碼庫代表著項目的健康。保持項目健康是如此重要,因此CruiseControl必須以最引人注目的方式告訴整個團隊:我們的項目現在怎么樣了。為此,各個團隊想出了種種辦法。我們可以用一個客戶端工具來監(jiān)控CruiseControl的構建情況,然后讓這個客戶端在發(fā)現構建失敗時調用別的程序:唱一段歌,發(fā)出怪叫,或者點亮一盞燈……總之,讓大家都知道。
圖3:紅燈/綠燈,構建結果一目了然
“敏捷宣言”中清楚地寫著:“個體與交互勝于過程與工具”。對于一個敏捷團隊來說,最重要的事情莫過于建立有效的交流渠道。在ThoughtWorks,每個項目都有自己的wiki,開發(fā)者、項目經理、客戶……所有的項目涉眾都會把自己的收獲與心得記錄在wiki上,以便大家分享和交流。
開源的wiki很容易找到,我在這里不必多說。我唯一想要介紹的,是這個叫做TiddlyWiki的小東西。這是一個完全用HTML和JavaScript編寫的wiki,使用它甚至不需要服務器,你只要在本地打開這個HTML文件,就可以開始寫你的wiki了。你所寫的內容同樣會被保存在這個HTML文件中,并且還提供了修改跟蹤的能力。使用這個wiki,你可以把整個文檔放到你的版本控制系統(tǒng)中,不需要為它做任何額外的配置。
另外,別忘了一個最重要的工具:紙卡片——你可以隨便寫,用任何顏色寫,可以畫任何你想畫的圖,可以用任何順序來排列粘貼它們,可以任意移動,可以隨時加上注釋,誰都知道怎么使用它們。你甚至還可以撕掉它們,優(yōu)質的紙卡片撕起來有一種特別的手感……好吧,紙卡片不是軟件,通常也不免費,但你知道怎么得到它們。
圖4:ThoughtWorks——值得信賴的全球紙卡片提供商