面對遺留系統,選擇合適的測試策略,能讓自動化測試的投入在一定時期內看到效果,并且建立可持續進行的機制。同為自動化測試,每種測試在面對遺留系統時遇到的挑戰是不同的,起到的效果也不盡相同。
背景
我目前所服務的企業大部分系統是遺留系統,其中多數處于相對需求平穩階段,即需求并不多,也沒有大需求。但這些系統牽制了和需求所需人力不成比例的大量人力,從系統本身的原因看,有這么幾點。
系統晦澀難懂,可讀性可理解性很差。理解原有系統往往占據了進行一個修改的大部分時間。
系統設計僵化,改動困難,一個小修改,會迫使系統很多部分的改動。
系統難以重用,大多軟件單元缺乏可重用性。
系統脆弱,引入一個小功能會引入幾個缺陷,修復一個缺陷又引起幾個新缺陷。
投入大量人力,產生的價值卻微乎其微。面對激烈的市場,同質化的競爭,成本和質量問題日益凸顯。而所謂遺留系統,即沒有自動化測試保護的系統??蛻艉芟Mㄟ^引入“自動化測試”來提升系統質量,最終幫助他們建立自動化測試機制。
過去幾個月里,我先后投入到幾個遺留系統的自動化測試提升工作中。這些工作都進展不錯,很多系統的核心模塊都有了自動化測試的覆蓋。另外,這次專門針對“遺留系統”所做的自動化測試工作,也帶給我一些新想法:自動化測試,很可能是我們撬動遺留系統的一個支點。
測試策略選擇
圖1是測試金字塔模型,從上至下分別是驗收測試、API測試、集成測試和單元測試。你可以有不一樣的分類,但從上至下,測試粒度越來越小,測試數量越來越多。一個具備完善自動化測試的系統,應該具備類似的測試分布。
圖1 測試金字塔模型
當我們面對的是遺留系統時,追求理想模型肯定是不現實的,那么應該選擇何種測試策略呢?
每 個遺留系統的狀況都不盡相同,可能選擇的策略也會不一樣。但有一點是一致的——所有的測試都不是沒有成本的,在人力并不寬裕的情況下,必須讓測試投入“值 回票價”。而且,必須讓測試投入到短期最有價值的地方,才能讓團隊盡快看到效果和建立信心。我們選擇的策略之一是:快速建立穩定性較高的功能測試防護墻, 以此保護系統的核心功能不被任意破壞。這里有兩個關鍵點。
快速,要選擇可以快速建立的測試,讓一定的價值在短期之內就能得到體現。
穩定,指的是測試本身的穩定——不因系統變化而對測試產生太大的沖擊,因而維護成本也就相對較低。
這里的功能測試可以是驗收測試、API測試或集成測試,根據每個系統的情況選擇更好滿足上面兩個關鍵點的測試類型。
例如,我們曾面對一個Web系統,大部分頁面邏輯比較簡單,主要是呈現內容;前端通過REST接口跟業務后臺交互數據。剛開始我們選擇基于 WebDriver的驗收測試,但隨后即發現這類測試編寫成本較高,需要學員掌握較多技能,無法在短期內快速為整個系統建立一個防護墻;另一方面,它的穩 定性也較低,測試較易受到頁面布局的影響,維護成本較高。在這種情況下,最后我們轉而選擇了基于REST接口的測試,因為它的建立成本更低,穩定性也更高 (REST接口變化較少),而且也可以覆蓋所有核心功能,相比而言,是個更好的選擇。
我們還選擇了另一個策略:針對熱點區域(包括需求熱點和缺陷熱點等)添加測試。選擇這些區域主要基于兩點理由。
首先,“非熱點”區域,也就是暫時穩定的區域,在初期并不是最值得投入為其建立測試的。例如,有個Web系統,它有兩個相對獨立的組件,一個是社區,另一個是網店,如果前者是熱點區域而后者不是,那么前者就更有價值在初期投入建立測試。
其次,遺留系統的脆弱性往往體現在“Bug重復出現”、“解決一個Bug,出現兩個Bug”等情形。針對這些活躍區域添加測試可以對它們起到保護作用,減少出現上述情況出現的機會;同時,也是對這塊區域的一個重構契機。
針對“熱點”區域,我們一般會在團隊中建立類似“完成新需求必須同時完成測試”、“修復缺陷必須添加測試”這樣的紀律。
同時,選擇合適粒度的測試也很重要。各類測試自己的優點,例如集成測試在功能保護上體現效果更快;而單元測試卻會驅動內部質量的提升。如果條件允許,選擇多 種粒度的測試結合,別忘了之前提到的測試金字塔。我們無法為整個系統一下子建立完善的測試,但為某一個區域,是有可能的。
為遺留系統寫功能測試
功能測試處于測試金字塔的上端,它的穩定性相對較低,維護成本也較高。因此寫功能測試一定要關注提升它的穩定性,并降低維護成本,遺留系統在這幾個方面遇到的挑戰可能會更大。
最近我對一個Web系統建立基于WebDriver的功能進行測試,其中面臨的一個很大問題就是HTML頁面缺乏語義、很多元素的定位都得依靠位置等極不可 靠方式,一旦頁某些布局發生變化,就會影響到測試,維護成本很高。但事情總有兩面性,正是這些測試,讓頁面的重構和優化得到了團隊的重視。
影響功能測試穩定性的另一個重要因素是測試數據。對于團隊控制范圍內的系統,我的建議是隨著測試的建立逐步創建一套可靠的、覆蓋各種典型場景的測試數據準備腳本。由此,我們每次都重新建立干凈的測試數據,讓測試更加穩定和可控。
但在遺留系統中,有時會碰上更嚴峻的問題,系統依賴于第三方或其他不在控制范圍內的測試系統。功能測試會影響到測試數據,因此我們的測試很有可能無法重復執 行。當然,建立一個測試替身系統是一種選擇方案,但有時并不容易,至少短期之內。面對這種情況,我們的解決方案是讓測試程序和測試數據解耦。想象一下,如 果同樣的測試由一個測試人員手工執行,每次執行時不需要選擇相同的數據,而只需選擇“符合同樣要求”的數據。
例如一個電商系統,它出售數量 有限的商品,售完即止。測試數據庫中有大量不同商品,但每種商品數量所剩無幾。如果我們的商品購買測試程序針對某個特定商品,那么在運行幾次之后,商品就 會賣完,測試就不再具備可執行性。但測試人員不會這么傻,他每次都可以選擇還有剩余的商品進行購買測試。既然如此,我們的測試程序也同樣可以做到:只要根 據商品頁面上的信息識別出哪些商品有剩余,隨機或者有策略地選擇其中某個商品進行購買即可。
原文轉自:http://www.programmer.com.cn/14624/