自動化測試最佳實踐 連載三
Lisa Crispin
瀏覽“如何閱讀本書”和“案例研究反思”,了解本書章節(jié)安排。
Lisa Crispin以其特有的迷人方式描述了當一個敏捷團隊決定實施自動化測試時所發(fā)生的事情。由于Lisa在敏捷技術(shù)方面的專業(yè)能力,當看到這支團隊在實踐中確實非常敏捷時,我們一點兒也沒有感到驚訝。這個項目中一件有趣的事情就是:團隊(小型團隊)里面的每個人都參與了自動化。他們不僅擅長敏捷開發(fā),而且非常敏捷地對其進行了自動化——并且他們成功了。實施敏捷開發(fā)并不是這支團隊取得成功的唯一要素,其他要素也同等重要,其中包括通過良好的溝通建立穩(wěn)固的管理關(guān)系,以及建立自動化平臺來支持創(chuàng)造性的手動測試。另一個關(guān)鍵要素是在團隊將過程改進嵌入到整個流程的決策力,包括一年兩次的自動化重構(gòu)的安排。毫無疑問,Lisa和她的團隊在他們第一年的時間里所取得的成就是非常顯著的。這個項目是為一家美國公司的財務(wù)部完成的,特征見表1-1。
表1-1 案例研究特征
(續(xù))
1.1 本案例研究的背景
我們必須面對這樣的事實:對于從未進行過自動化測試的人來說,自動化測試是具有一定難度的。本故事告訴我們,面對無任何自動化的測試和有著糟糕設(shè)計的遺留系統(tǒng),這支團隊通過一年多的努力,將所有的回歸測試都實現(xiàn)了自動化。在接下來的幾年時間里,我也與數(shù)十個其他面臨同樣困境并找到類似解決方案的團隊進行了交談。看看我們所遇到的這些困難是否與你所遇到的相似,并考慮用類似的方法進行嘗試。
1.1.1 問題
從這里開始著手:每兩周我們都需要把新的功能添加到產(chǎn)品中,但是代碼bug成災(zāi)并且也沒有自動化測試,更嚴重的是,產(chǎn)品中有大量隨時會導(dǎo)致系統(tǒng)中斷的bug。我們?nèi)绾螖[脫這種情況呢?
1.1.2 目標
我們決心盡自己所能編寫出最高質(zhì)量的代碼,但是從哪里開始呢?作為一支自組織的敏捷開發(fā)團隊,讓我們感到欣慰的是整個團隊的緊密協(xié)作。那是在2003年,我們中有些人在別的團隊中曾有過良好的自動化測試經(jīng)驗,他們相信總是會有辦法的。我們發(fā)現(xiàn),一個安全的自動化回歸測試網(wǎng)絡(luò)可以讓我們更快速地工作。如果我們知道是由于某段特殊的代碼而引入的非預(yù)期的操作,那么我們能夠立即穩(wěn)定我們的代碼庫。通過充分的測試覆蓋來不斷進行集成,使我們每天都有一個穩(wěn)定的構(gòu)建過程。而在迭代的后期,現(xiàn)在很難得到穩(wěn)定的集成,所以這個想法雖然并非那么容易實現(xiàn),但聽起來很不錯!
接下來,看看到底是什么幫助我們創(chuàng)建了一個成功的策略來實現(xiàn)自動化回歸測試套件。
1.2 整個團隊的承諾
我們是由多個程序員、一個測試人員、一個數(shù)據(jù)庫管理人員、一個系統(tǒng)管理人員和一個敏捷教練所組成的團隊。公司的業(yè)務(wù)專家可以隨時協(xié)助我們。我們整個團隊致力于在每次發(fā)布之前運行我們的手動回歸測試腳本。因為我們每兩周發(fā)布一次,這意味著兩周的迭代周期中我們要花1 ~ 2天的時間進行手動測試。在最終用戶使用之前,我們沒有足夠的時間來進行探索性測試,雖然這種測試可能會幫助我們找到嚴重的缺陷。
【真知灼見】
敏捷開發(fā)團隊中的每個人都進行手動測試,所以他們更能體會到自動化測試的好處。
我們都很關(guān)心質(zhì)量,并且我們都致力于保證所有的測試活動都是有計劃的且在每一次迭代中執(zhí)行。這是一個好的開始!
1.3 建立自動化策略
我們需要在不破壞現(xiàn)有功能的前提下發(fā)布產(chǎn)品的新功能特性。而且,需要盡快知道一個新的代碼變動是否會引起回歸測試的失敗。手動回歸測試在每兩周的迭代后期才能給予我們反饋,以至于沒有時間進行充分的回歸測試。
我們中一些人曾經(jīng)在其他敏捷團隊中進行過測試驅(qū)動開發(fā)(Test-Driven Development, TDD)。我們發(fā)現(xiàn)TDD能幫助創(chuàng)建出設(shè)計良好的、健壯的代碼。
我們現(xiàn)有的回歸測試是手動操作的,整個團隊通過使用團隊Wiki上所記錄的腳本進行手動測試。在每兩周的迭代周期中,這就花費了整個團隊的20%的時間。這些測試僅僅為程序最核心的部分提供了最小程度的覆蓋。產(chǎn)品中報告的缺陷表明,回歸測試的失敗仍有可能發(fā)生。在每次迭代周期中,我們至少要花20%的時間修復(fù)這些產(chǎn)品缺陷,從而限制了我們能開發(fā)的新功能的數(shù)量。
自動化的回歸測試會帶來更高、更準確的覆蓋率,這需要投入大量的時間、金錢和精力,我們可能需要硬件和軟件來建立構(gòu)建過程、持續(xù)運行測試所需要的環(huán)境,我們也需要使測試實現(xiàn)自動化的架構(gòu)和驅(qū)動。然而,我們可以計算出這一自動化將會節(jié)省我們40%的時間,利用這些時間可以進行更多有價值的活動,比如進行新的開發(fā),所以,其收益遠大于成本。
繼續(xù)進行手動回歸測試必將會失敗,我們需要一個明確的決策來進行自動化。因為我們都在進行手動回歸測試,每個人都感受到了沒有自動化測試的痛苦。所以,我們有了解決這一問題的動力。首先,我們需要一個可測試的架構(gòu)……
1.3.1 一個可測試的架構(gòu)
TDD這條路是要走的,但是當前的代碼在業(yè)務(wù)邏輯、數(shù)據(jù)庫訪問和UI等處相結(jié)合時,情況比較復(fù)雜,自動化單元測試也就變得很難了。往往很難隔離任何一個組件單獨進行測試。
這樣看來,似乎找到一種把軟件進行分層的新架構(gòu)是非常明智的。我們開發(fā)出了這一新架構(gòu)的所有新功能。
【小竅門】
不要嘗試解決老問題,進行新的開發(fā)來開展更好的實踐。
如果進行自動化測試的成本比其收益高,那么進行自動化測試就沒什么意義。我們研究圖1-1所示的自動化測試金字塔(這一金字塔是由我們當時的經(jīng)理Mike Cohn提出的)。單元級別的測試一般ROI最高。程序員可以很快寫出它們并運行,而且測試可以根據(jù)需要進行更新。因此可以將單元級別的測試作為自動化回歸測試的堅實基礎(chǔ)。
我們的業(yè)務(wù)邏輯相當復(fù)雜,而且新架構(gòu)將這一邏輯從數(shù)據(jù)庫和用戶接口層中分離出來,這樣就可以通過設(shè)置內(nèi)存中數(shù)據(jù)并在其上運行產(chǎn)品代碼來進行測試。這是金字塔的中間層———比底層運行的測試要少但是依然很重要。
我們還需要測試UI,但是通過UI進行的自動化測試本身就非常脆弱、維護費用高且運行緩慢。因為最終想使UI測試所占的比例盡量最小,所以它就處在金字塔的最頂端。盡管如此,與其他團隊一樣,我們從圖形用戶界面(GUI)冒煙測試(smoke test)開始,來獲取一些代碼防護。所以,從這個角度,我們的金字塔是上下顛倒的,但是沒關(guān)系———最終我們會將它翻轉(zhuǎn)過來。(我們最終花了4年時間才獲得為之努力的三角形形狀!)
圖1-1 自動化測試金字塔
【小竅門】
解決問題的最直接途徑未必是最佳途徑。
我們現(xiàn)在明白了我們需要做什么,所以我們開始著手實施那個能給我們帶來最大實惠的任務(wù)。
1.3.2 建立構(gòu)建過程
我們只有4位程序員,但是他們會經(jīng)常檢查源代碼控制系統(tǒng)中的變化。我們需要確保他們沒有不小心修改別人的內(nèi)容或者破壞現(xiàn)有的任何功能。同時,我(測試人員)有時候不得不等待好幾天,部署在測試環(huán)境中的代碼才能完成一次構(gòu)建。一個自動化的構(gòu)建過程(build process)將保證在每次檢入后的幾分鐘內(nèi),最新代碼的可部署版本就是可用的。
管理層向我們強調(diào)了質(zhì)量是我們的第一目標,他們樂意寬限一些時間來讓我們搭建一個好的基礎(chǔ)結(jié)構(gòu)。
【真知灼見】
好的管理者會準許團隊花時間去開發(fā)自動化的基礎(chǔ)結(jié)構(gòu)。
我們停下正在做的事情,使用CruiseControl和一個新的Linux服務(wù)器建立了一個持續(xù)集成(Continuous Integration, CI)過程。因為我們還沒有可以運行的任何測試實例,所以只是簡單編譯代碼和構(gòu)建可部署的二進制文件。我可以看到每次構(gòu)建的檢入文件并能夠選擇想部署到產(chǎn)品中的二進制文件。這很有用,但是還不夠。
1.3.3 獲取測試的基準:GUI冒煙測試
程序員正在學(xué)習如何使單元測試自動化并編寫測試先行(test-first)的代碼,但是要真正實施測試驅(qū)動開發(fā)需要花好幾個月的時間。針對遺留代碼,使用GUI冒煙測試可能是一個快速獲得自動化測試覆蓋的途徑。但是使用什么工具最好呢?
我們有購買一個付費工具的預(yù)算資金,但是團隊里的程序員是Java程序員,他們?nèi)f不得已是不愿意使用另一種腳本語言來進行測試的。捕獲/回放并不適合我們,因為我們需要可維護的、穩(wěn)定的測試腳本。我們選中了 Cannoo WebTest——一個可以讓我們修改XML文件中的測試用例,并使用Ant運行它們的開源框架。而且它很容易與我們的構(gòu)建過程集成。
【真知灼見】
昂貴的商業(yè)工具不一定是最好的選擇。
我讓業(yè)務(wù)專家把需要用冒煙測試來保護的系統(tǒng)核心區(qū)域按優(yōu)先等級進行劃分。每個沖刺時間段里,都安排了時間讓我用WebTest工具自動運行測試腳本。我們首先追求的是“快贏”,針對系統(tǒng)中每個用戶角色的基本功能實現(xiàn)自動測試。
首先, CI構(gòu)建過程只運行了少量單元測試和一些覆蓋系統(tǒng)高得分點的GUI冒煙測試。隨著在這兩個級別引入更多測試,我們將GUI測試移到單獨的構(gòu)建過程中并只在晚上運行,這樣,我們可以更快得到的反饋信息。
與此同時,我們將單元測試和每個新用戶故事(user story)的GUI冒煙測試都放在迭代中完成。新的功能將會在自動化的單元測試和GUI測試中被覆蓋。一旦程序員能夠熟練使用TDD,我們就將進入金字塔的中間層。
1.3.4 在單元級別驅(qū)動開發(fā)
我們的程序員中只有一人曾經(jīng)實施過TDD,但每個人都支持這種理念。我們根據(jù)別人的介紹,找到了我們能找到的最好的顧問來幫助我們學(xué)習如何實施TDD,很多時候我們都是自帶午餐以便擠出時間來進行TDD試驗。但是很遺憾的是:它很難學(xué)!我們團隊需要時間來掌握它。
我們的管理層知道這個道理:目標是寫出優(yōu)美的代碼,優(yōu)美到可以拿回家給媽媽,當做冰箱貼。我們的公司——一個剛剛成立3年的商業(yè)公司,因為網(wǎng)絡(luò)應(yīng)用問題和無能力及時發(fā)布新功能而面臨倒閉。我們的商業(yè)合作伙伴也準備放棄我們了,但自從我們實施敏捷開發(fā)(Scrum)以后,他們看到了我們?yōu)樘岣叻€(wěn)定性和響應(yīng)能力所做的努力。我們的執(zhí)行者致力于具有長遠收益的投資。
【經(jīng)驗教訓(xùn)】
一個迫在眉睫的災(zāi)難可能是巨大進步的推動力。
我們知道這些單元級別的測試具有最好ROI。我們也理解TDD事實上是代碼設(shè)計,而不是測試。考慮“代碼是用來做什么的”可以幫助程序員寫出正確的代碼,而快速實施測試,才能及時地提供重要的反饋信息。
單元級別測試的最大好處就是能夠提供最快速的反饋。在研究一些好的實踐之后,我們決定將代碼構(gòu)建過程的時間控制在10分鐘內(nèi)。這需要單元測試的隨機重構(gòu)。早些時候,單元測試需要訪問數(shù)據(jù)庫,之后,程序員掌握了如何構(gòu)造、模擬它,以及消除它的影響,使測試快速運行又能提供正確的測試覆蓋。
1.4 利用驗收測試驅(qū)動開發(fā),使用FitNesse測試GUI
現(xiàn)在已經(jīng)是我們自動化之旅的第8個月了,程序員已經(jīng)建立了一個自動化單元測試的實用庫。對于應(yīng)用程序的核心區(qū)域我們已經(jīng)進行了冒煙測試,覆蓋微量代碼的大約100個JUnit測試已經(jīng)完成了。但是中間層還什么都沒有,TDD此時變成了一個空殼。現(xiàn)在我們開始對自動化測試金字塔的中間層進行填充。
1.4.1 內(nèi)存內(nèi)測試
我們的金融理財產(chǎn)品有許多復(fù)雜的算法,這可以通過在內(nèi)存中提供輸入來進行測試。與單元測試相比,這種測試的級別要高很多,但我們還是不想通過GUI來進行測試,因為其速度慢、成本高。我們發(fā)現(xiàn)可以很迅速、方便地編寫FitNesse夾具(fixture)來自動進行這類測試。在客戶測試層次,我們開始用FitNesse測試來驅(qū)動新的用戶故事的開發(fā)。這些測試使用夾具在內(nèi)存中構(gòu)建測試輸入,并把這些輸入發(fā)送到應(yīng)用層代碼,就如同產(chǎn)品在實際使用時進行的輸入一樣。然后夾具會返回代碼的實際輸出,并把它與FitNesse表中的期望結(jié)果進行對比。
測試人員和客戶填寫FitNesse測試用例,之后程序員用夾具來自動運行它們。這意味著我們需要交流!提高團隊的交流能力是使用這種工具進行自動化測試的最大好處之一。我們測試人員與產(chǎn)品所有者和其他利益相關(guān)者坐在一起分析每個用戶故事預(yù)期的和非預(yù)期的行為。我們將這些用戶故事轉(zhuǎn)化為FitNesse測試用例表,并與客戶核對以保證當測試用例通過測試的時候能夠滿足客戶的要求。我們與開發(fā)人員核對測試,以保證我們清楚需求并保證測試設(shè)計與代碼設(shè)計兼容。開發(fā)人員編寫夾具來自動運行測試。這一過程在單個用戶故事的很多小的迭代中不斷重復(fù),直到開發(fā)和測試都完成。 1.4.2 使用數(shù)據(jù)庫的測試
我們的應(yīng)用是數(shù)據(jù)密集型應(yīng)用,我們想自動運行更加端到端(end-to-end)的測試。我們也可以使用FitNesse在數(shù)據(jù)庫中構(gòu)建測試數(shù)據(jù),并使用遺留代碼在它上面運行,以此來測試遺留代碼。
這種類型的測試腳本編寫和維護都更加昂貴。作為FitNesse的初學(xué)者,我們犯了一些錯誤,如,不知道將測試組件模塊化,而這可以通過FitNesse中現(xiàn)有的部件來完成。我們徹底違反了代碼設(shè)計中的“不要重復(fù)自我”準則。例如,在幾十個不同的測試頁面中,有包含同樣員工信息的表,如果又有一列新數(shù)據(jù)需要添加到員工信息表中,那么在每個測試頁面中都要加進去,這很糟糕。等到我們知道如何正確去做的時候,又遇到了一個更難以處理的麻煩。
【經(jīng)驗教訓(xùn)】
在前期進行工具使用的培訓(xùn),之后就可以避免因工具使用不熟練而造成的巨大的時間浪費。
我們將每個能想到的測試用例都進行了自動化,包括發(fā)生幾率低、影響小的邊緣測試用例,并把它們都放在自動回歸測試套件(test suite)里面。上述過程花費的時間并不長,但是接下來,這個測試套件要花費大量時間和主機功率(machine power)來運行,維護成本也隨之提高了。所以,我們要學(xué)會慎重選擇測試用例,確保它們能提供充分的測試覆蓋,并只將這些測試用例放在回歸測試套件中。
【小竅門】
精化回歸測試套件可以在保證收益的同時降低維護費用。
正如在產(chǎn)品應(yīng)用代碼中所做的那樣,我們現(xiàn)在不斷地重復(fù)訪問和重構(gòu)FitNesse測試用例,以保證我們所需要的測試覆蓋,同時不會過多地延長反饋周期或者花費太多時間維護測試。
【真知灼見】
經(jīng)常檢查自動化測試用例以保證它們是有效的。
1.4.3 使用FitNesse測試的好處
我們的FitNesse測試提供了比GUI測試套件更快的反饋,盡管比JUnit測試要慢很多。FitNesse測試套件需要60 ~ 90分鐘來運行,而JUnit測試的運行時間僅僅只需要不到8分鐘。像GUI測試腳本那樣,我們將FitNesse測試集成到構(gòu)建過程中,并且在其中運行。一開始,我們只在晚上運行這種“完全構(gòu)建”(full build),但這無法提供及時的反饋,并且如果測試失敗的話,我們只有在第二個晚上才能知道問題是否修復(fù)了。我們投入了更多的硬件,這樣我們就可以“連續(xù)地”運行單元級別上的所有測試的完全構(gòu)建。如同運行單元級別測試的構(gòu)建一樣,這是為了使源代碼控制每接收到一次檢入就能運行。大概需要90分鐘運行一次,所以經(jīng)常同時測試幾個新的檢入。
(未完待續(xù)...)
相關(guān)鏈接:
posted on 2013-04-23 10:42 順其自然EVO 閱讀(329) 評論(0) 編輯 收藏 所屬分類: selenium and watir webdrivers 自動化測試學(xué)習