iThome

在20世紀50年代,美國加州的交通部深受高速公路車道分隔線的問題所困擾。分隔線會褪色,所以每一季都需要重新噴塗。不僅費用高昂,還會擾亂交通,對噴塗的工人而言也很危險。

Elbert Dysart Botts博士致力於解決這個問題,他做了許多試驗,嘗試使用更多反光漆,但最終都證明那是死路一條。跳出框架思考後,他發明了突起的車道標記,名為『Botts點』。無論是什麼天氣,白天還是黑夜都能看得到Botts點。它們不像噴塗的分隔線容易褪色。若司機越過指定的車道時,除了視覺,司機還可以感受到Botts點產生的振動及隆隆的聲響。這已被認為是高速公路上最重要的安全特性之一,當漫不經心的司機偏離車道時即可警告他們將要發生危險。

Botts點被導入軟體開發中,成為了12個XP的初始實踐做法之一,被稱為持續整合(Continuous integration)。當疏忽大意的軟體團隊偏離了可建置及交付的產品開發原則時,『持續整合』就會發出警示。專用的持續整合系統會頻繁地建置產品並執行測試以確保系統不僅在開發人員的機器上正常運作。此實踐做法透過快速地找到潛在問題讓我們保持在正確的道路上,在必要時也只需採取小型而廉價的糾正措施。持續整合可以確保產品在被正確地建置起來後,能始終保持正確。

相同的原則也可以應用到建置正確的產品上。一旦建置了正確的產品,我們就要確保它能保持正確。當它偏離設計的方向時,只要能儘早地發現,不讓問題累積起來,就可以更容易且更廉價地解決問題。我們可以頻繁地驗證可執行的Spec.(Executable specification)。持續建置(continuous-build)的伺服器可以頻繁地檢查所有的Spec.,並保證系統始終符合需求。

持續整合是個擁有完備說明文件(Documentation)記錄的軟體實踐,已有其他許多的作者對它作了詳盡的描述。我不想籠統地重複描述『如何設置持續建置與整合的系統』,但是關於一些可執行的Spec. 中,頻繁驗證的挑戰是本書的重點主題。

許多團隊使用Spec.實例化(Specification by example,SBE)來擴展現有系統,他們發現可執行的Spec. 必須在真實的資料庫上運行,意即需要具有真實的資料、外部服務或部署完整的網站。功能驗收測試會透過許多元件來檢查功能,如果系統事先並不具有可測試性,那此類檢查往往需要先對整個系統進行整合與部署。除了一般持續整合用到的技術性的(單元)測試,上述情況還會帶來其他3組關於頻繁驗證的問題:

● 因具有環境的依存性而導致的不穩定性:單元測試在測試環境中大部分是獨立的,但可執行的Spec. 可能強烈的依存於運行時的生態系統。即使程式碼正確,環境問題也可能會導致測試失敗。為了對驗收測試的結果更有信心,我們必須解決或減少這些環境問題,使測試執行更為可靠。

● 回饋較慢:針對修改或重建過的專案,其功能驗收測試的運行速度通常會比單元測試慢許多。如果單元測試的執行需要幾分鐘,我認為它是比較慢的了。將近10分鐘的單元測試絕對是太慢了,我會很認真地研究如何提高速度。另一方面,我見過許多驗收測試需要長達數小時的執行時間,除非犧牲測試的可信度,否則就難以最佳化。整體回饋如此慢的情況下,我們需要想出一種解決方案,使得系統某些指定的部分可以按要求提供快速的回饋。

● 管理失敗的測試:大量的功能測試力度較粗且依存於許多活動的部分元件中,這需要一些團隊(特別是剛開始實行Spec. 實例化的團隊)管理失敗的測試而非直接展開修復。

提高穩定性

不穩定的驗證程序會削弱團隊對產品和Spec. 實例化程序的信心。調查那些間歇性的又不是由真正問題導致的失敗會浪費大量的時間。如果這種事情經常發生,開發人員就有了根本不去查看與驗證問題的藉口。這會讓真正的問題逃過檢測,使整個持續驗證變得沒意義。

遺留系統(Legacy system)的專案的自動化功能測試基本上沒有比較容易的方法,因此,可執行的Spec. 可能需要透過不可靠的UI(使用者介面)來自動化,或受到非同步性程序導致的不確定性影響。當開發人員必須有說服力的參與程序,但其只將它當作是功能測試的改進版(換句話說,他們不認為他們必須參與)時,問題就會變得非常棘手。

Clare McLennan和她的團隊就遇到這個問題。「因為測試本身不穩定,所以開發人員不關心這些測試。但我們需要開發人員的知識才能使測試變得更穩定。」對她的團隊來說這是個『先有雞,還是先有蛋』的問題。為了讓開發人員參與進來,她必須讓開發人員看到可執行的Spec. 的價值。但是,只有穩定且可靠的可執行的Spec.才能展現出它的價值。而Spec.要想穩定可靠,則需要開發人員修改系統設計,使系統易於自動化測試時才能達成。

為了獲得Spec. 實例化的長遠效益,許多團隊不得不投入大量的精力讓驗證程序變得更可靠。以下我將闡述關於這方面的一些好主意。

找出最煩人的問題並將其解決掉,並且重複此步驟

適用於:自動化測試的支援系統不夠好時

想在系統上更可靠地執行自動化測試,最重要的一點就是要明白:改變不會一夜之間就發生。遺留系統是不容易改變的,否則它也不會叫做「遺留」系統了。建置了如此多年,系統一直都未曾有過「可測試性」的設計時,想要瞬間變得乾淨並具有可測試性是不可能的。

快速導入太多重大的修改反而會破壞系統,尤其是當我們沒有良好的功能測試覆蓋率時。它會嚴重打斷開發流程。

 HINT  不要試圖一下子就解決問題,利用迭代式(Iteratively)累積許多小規模的變更是更有效的策略。

例如,McLennan的團隊意識到,測試資料的緩慢程序會導致測試超時。他們的資料庫管理員提升了資料庫的性能,這讓他們發現有些測試在測試資料更新前就開始了,所以系統一直在用舊的資料。他們推出訊息機制,用以提醒測試『資料庫裡準備好最新資料了』,所以他們之後就可以放心的啟動測試,並避免掉假性的誤報(false negatives)。『不可預測』的來源解決之後,他們發現HTTP cookie過期導致了一些問題。於是他們推出『業務時間』的概念,讓他們就可以修改系統的時間。

McLennan建議以『漸進』的方式當作提高自動化測試穩定性的實作策略。

找到最煩人的問題並將其解決掉,別的問題又取而代之成為最煩人的問題,再解決後,目標再次轉向另一個最煩人的問題。如此不斷重複,最終的系統將會是非常穩定且非常有用的系統。

以迭代式(Iteratively)來累積的方法提高穩定性是建置可靠的驗證程序的好方法,而且其又無需中斷交付流程。這種方法還讓我們在提高系統可測性期間可以不斷學習並適應。

由持續整合測試歷程找出不穩定的測試

適用於:在遺留系統中加裝自動化測試時

對於不具有自動化測試的遺留系統而言,存在太多導致不穩定的地方,想要以迭代式(Iterative)的清理方式通常仍是無從下手。有個不錯的方法是查看測試的執行歷程。現今持續建置的系統大部分都具有過去一段時間追蹤測試結果的功能。

  HINT    只要可執行的Spec. 在持續建置的系統中運行,我們就可以從測試的運行歷程中看到哪些測試最不穩定。

多年以來,我完全疏忽了這個功能,因為我參與的大多是已預先考慮可測試性的新專案,或是只需少量的修改就可以十分穩定的系統。這些專案中,追蹤測試執行歷程沒什麼用處:測試幾乎一直都是可以通過的,即使失敗也很快就修復了。我第一次嘗試改善自動化測試的系統,是一個偶爾有超時問題、網路問題、資料庫問題以及不一致問題的程序,查看測試歷程能幫助我集中精力提高穩定性。此功能告訴了我哪些測試最常失敗,因而必須最先修復。

設置專門用來持續驗證的環境

  HINT  如果你的應用程式需要部署,並且要運行功能測試,那麼為確保可重現性的第一步就是設置專用的部署環境。

持續驗證必須以可重現的方式來工作,這樣才可靠。一些大型的組織中,獲取一批新機器要比雇用有名的投毒殺人犯擔任公司自助餐廳的大廚還要難,但是爭取較好的設備依然是值得的。許多團隊使用同一個環境做各種的用途,例如,為商務使用者展示功能、手動測試以及持續驗證。這往往會導致資料一致性的問題。

若沒有專用的環境,很難知道測試失敗是因為有缺陷、有人對測試環境做了變更,還是系統不穩定所導致的。專用的環境可以排除計畫以外的變更,並降低環境不穩定的風險。

採用完全自動化部署

一旦我們有了專用環境,就必須確保軟體用了可重複的方式部署。『不可靠的部署』是測試結果不穩定最常見中的第二名。對於許多遺留系統,部署是需要一整晚時間的程序,需要多名人員、大量的咖啡,最好還要有個魔法棒。如果我們每年只需部署一次,那還可以接受。然而,當每兩週都需部署一次時,就是一件非常頭疼的事情了。為了持續驗證,我們可能需要一天部署多次,需要魔法相助的手動部署是令人完全無法接受的。

部署完全自動化能夠可靠地升級系統,若沒有,我們就會頻繁地碰到這種情況:許多測試突然開始失敗,有人得花上數小時來尋找罪魁禍首,卻又會聽到有人說「但這在我機器上是好的」。

  HINT  部署完全自動化可以確保升級的只有唯一的標準程序,同時又可以確保所有的開發人員擁有和測試環境一樣的系統部署。

這排除了可執行的Spec. 對特定環境的相依性,可以大幅提高持續驗證的可靠性。同時也能更容易排除故障,因為開發人員可以使用任何環境來重現問題。

部署必須完全自動化才行。完全無需或不允許手動操作。(請注意我說的是,可以依照需要執行完全自動化的部署,但不一定要能自動觸發部署。)如果安裝程式需要處理管理員控制台或半自動化的手動腳本,那麼它就不算完全自動化。特別值得一提的是,部署完全自動化還包括自動化資料庫部署。

我見過許多團隊宣稱自己擁有自動化的部署,結果發現需要有人手動運行資料庫腳本。

部署完全自動化還可以帶來其它好處,例如,可以更容易地升級生產環境。長遠來看,這將為你節省大量的時間。不管是否使用Spec. 實例化,頻繁部署都是一個非常好的實踐做法。

加速獲得回饋

已有大多數團隊發現,每次系統改變後把所有可執行的Spec. 都再重複一遍是不太實際的想法。如果測試包含大量的檢查(特別是只能針對網站、資料庫或外部服務運行的檢查時),那麼從完整的測試執行中獲得回饋就太慢了。為了更有效地支援開發並輔助變更,大多數團隊都對他們的持續驗證系統進行了變更,以便提供階段性的回饋,這樣就能加速得到最重要的資訊。以下是一些團隊的使用策略,它們能有效縮短回饋時間。

為當前的Iteration建立一個測試包

將執行時間較長的測試分割成較小的測試包有一個常見的特殊情況,就是為當前的Iteration建立測試包。這個測試包也涵蓋了當前開發階段會影響到的可執行的Spec.。

  HINT   當前Iteration的變更,會影響系統中最不穩定和最重要的部分,清楚劃分出當前Iteration測試包可以讓我們快速地獲得這部分的相關回饋。

如果我們把當前Iteration的測試包從其他測試中分出來,那即使是針對那些已計畫而尚未執行的功能編寫測試,我們也可以放心地將它們加入進去。執行當前Iteration的所有測試可以讓我們更容易地追蹤開發進度,準確地瞭解我們何時能夠完成。大部分的時間,當前Iteration的測試包可能會全部失敗,但這並不會影響主要的回歸驗證。

如果我們需要更頻繁地驗證這類型的Spec.,那可以使用這種方法,為當前版本建立一個測試包。請注意,許多自動化工具允許我們建立並行(parallel)的分層結構,如此一來,相同的Spec. 將可以同時隸屬於多個不同的測試包。

管理失敗的測試

《Bridging the Communication Gap》中,我提過:失敗的測試不應該直接被禁止掉,而是應該立刻修復。進行本書的研究後,略微改變了我的看法。有些團隊在持續驗證中累積了成千上百個檢查,這些檢查驗證了耗時數年才建置出來的功能。

頻繁驗證著大量的Spec. 會發現許多問題。另一方面,運行大量檢查往往表示回饋速率很慢,所以問題不會馬上被發現或解決。有些問題可能還需要商務使用者的釐清,或它們的優先順序與當前的Iteration將要上市的功能相比可能較低,所以不一定能夠在發現問題的第一時間解決所有的問題。

這也意謂持續驗證中有些測試會失敗,並且會失敗一段時間。當測試包失敗的時候,人們往往不會去探究它們可能導致的其他問題,所以將這些測試置之不理也不是個好辦法。以下是一些管理系統功能性回歸測試的竅門。

建立已知失敗的回歸測試包

類似於為當前Iteration建立獨立且可執行的Spec.,許多團隊專門建立預期是失敗測試的測試包。

  HINT   當你發現回歸測試失敗了,而你決定不要馬上修復,那麼請把相關測試加入到獨立的集合中,這樣即使測試失敗了也不會影響主要的驗證集合。

把失敗的測試單獨放在一起可以讓我們追蹤此類問題的數量,防止對暫時失敗的刻意放鬆規則轉變為「免費出獄」卡,而導致問題的堆積。

獨立的集合還能讓我們階段性執行所有失敗的測試。即使測試失敗,它們還是值得被執行的,因為這可以檢查是否有其他的失敗。在RainStor,他們把這些測試標記為缺陷,但是他們仍然會執行這些測試以便檢查進一步的功能回歸。Adam Knight說:

可能某天因為尾隨零(Trailing zero)的關係導致測試失敗了。如果第二天測試還是失敗了,你可能就不想檢查它了,因為你已經知道測試失敗了。但是它可能也回傳了一個完全錯誤的計算結果至系統中。

失敗的回歸測試包有個潛在的風險,就是它可能會在交付階段晚期成為品質問題的「免費出獄」卡。請把已知失敗的測試包只當作臨時空間使用,用來存放需要進一步查明的問題。『無法即時發現問題』是程序有潛在問題的警告標誌。避免落入此陷阱的較好策略是:限制加入已知失敗回歸測試包的測試數量。有些工具(例如:Cucumber)甚至可支援自動檢查這個限制。

建立單獨的測試包來收集所有失敗的測試,從專案管理的角度來說是好的,因為我們可以對其進行監控,並且在增長過快時採取措施。一、兩個不太重要的問題可能無須中止產品的發佈,但是如果這個集合增長到幾十個測試,那就應該停下來整頓,以免事情變得難以收拾。如果有測試在已知失敗回歸測試包裡放了很長的時間,那就表示此測試相關的功能也可以刪除了,因為根本沒有人關心這個功能。(摘錄整理自第十章)

 

想辦法從頻繁驗證中獲得更迅速的回饋:分開快速的測試與緩慢的測試,為當前Iteration的Spec. 建立測試包,把執行時間較長的可執行的Spec. 分成小型的集合。

 

Specification by Example中文版:團隊如何交付正確的軟體

Gojko Adzic/著;張昌貴、張博超、石永超/譯

博碩文化出版

售價:420元

 

作者簡介

Gojko Adzic

戰略軟體交付顧問,專精於敏捷與精實開發,尤其擅長敏捷測試、需求規格實例化、行為驅動開發。經常在國際上重要的軟體開發和測試會議上發言,並經營英國的敏捷測試開發小組。

熱門新聞

Advertisement