人人都認可開發人員測試的重要性,但為什麼運行測試還是需要花費太多時間?本月,Andrew Glover 揭示了三種用來確保端到端系統健壯性的測試類型,隨後展示了如何按類型來自動排序及運行測試。即使是使用當今大型測試套件,這樣做也能顯著地減少構建時間。
假如這樣說不會(令您)很痛苦的話,請設想您是一名任職於一家 2002 年早期創建的公司的開發人員。在金錢的驅動下,您和您的團隊接到了一項任務,即使用最新且最強大的 Java™ API 構建一個大型的數據驅動的 Web 應用程序。您和公司治理層都堅定不疑地相信這就是最終將被稱為靈敏過程 的東西。從第一天起,您就用 JUnit 構建測試,且把它作為 Ant 構建過程的一部分盡可能頻繁地運行。還將設置一個定時任務在夜間運行構建。在接下來的某個時刻,有人會下載 CruiseControl,不斷增長的測試套件會在每次簽入時運行。
時至今日
經過過去幾年的努力,您的公司已經開發了一個龐大的代碼庫和一個同樣龐大的 JUnit 測試套件。一切都很正常,直到大約一年前,測試套件包含了 2000 個測試,同時人們開始注重到運行構建過程用時超過三個小時。在此之前的幾個月,由於 CI 服務器資源緊張,您在代碼簽入時通過 Continuous Integration(CI)停止運行單元測試,並將測試切換到夜間運行,這使得之後的早晨時間非常緊張,於是開發人員努力去弄清楚是什麼出錯以及為什麼出錯。
這些天,似乎測試套件整晚極少超過一次運行,為什麼會這樣呢?因為它們費時太多!沒人會僅僅為了弄明白系統是否運行良好而幾個小時守在那裡。此外,整個測試套件都是在晚上運行,不是嗎?
由於測試運行得太不頻繁,它們經常布滿了錯誤。因而,您和您的團隊開始質疑單元測試的價值:假如它們對代碼質量那麼重要,那又為什麼會讓人這麼頭痛呢?你們的結論是:單元測試有其重要的作用,但必須要能用一種更為靈敏的方式運行它們。
嘗試測試分類
您所需要的是一個將構建轉換到一種更為靈敏狀態的策略。您需要這樣一種解決方案,使一天當中運行測試的次數超過一次,並使測試套件恢復到要用三個小時才能完成構建之前的水平。
為完整地恢復整個測試套件,在試圖提出一個策略之前,很有必要弄清楚通用術語 “單元測試” 的含義。諸如 “我家有一個動物” 和 “我喜歡車” 這樣的表述並不很具體,“我們編寫單元測試” 也是一樣。這年頭,單元測試能代表一切。
就拿之前有關動物和車的表述來說:它們導致了更多的疑問。例如,您家有哪種動物?是一只貓、一條蜥蜴還是一頭熊?“我家有一頭熊” 和 “我家有一只貓” 截然不同。同樣,當和汽車銷售員交談時,只說 “我喜歡車” 沒什麼用處。您喜歡哪種車:賽車、卡車還是旅行車?任何一個答案都能帶來截然不同的結果。
同樣,對於開發人員測試來說,按照類型 將測試分類也是很有用的。這樣做能夠實現更為精確的語言,並且能使您的團隊以不同的頻率運行不同的測試類型。為了避免運行所有 “單元測試” 所需的令人懼怕的三小時構建時間,分類是要害。
三種類型
測試套件可以形象地分為三層,每一層代表一種不同的開發人員測試類型,該測試類型由其運行時間的長短決定。正如在圖 1 中看到的那樣,每一層都增加了總的構建時間,要麼增加了運行時間,要麼最終增加了編寫時間。
圖 1.測試分類的三個層次
底層由運行時間最短的測試構成,可以想象的到,它們也最易於編寫。這些測試占用的代碼量也是最少的。頂層由更高級別的測試構成,這些測試占用了應用程序更大的部分。這些測試有一點難於編寫,執行時間也要長得多。中間層是處於這兩個極端中間的測試類型。
三種類型如下所示:
讓我們分別來看一下。
改進代碼質量
別錯過 Andrew 的相關 討論論壇,裡面有關於代碼語法、測試框架以及如何編寫專注於質量的代碼的幫助。
1. 單元測試
單元測試隔離地 驗證一個或多個對象。單元測試不處理數據庫、文件系統或任何可能延長測試運行時間的內容;因而,從第一天就可以編寫單元測試。事實上,這也正是 JUnit 設計的確切目的所在。單元測試的隔離概念有無數的模擬對象庫作後盾,這些庫便利了將一個特定的對象從其外部依靠項中隔離出來。而且,單元測試能夠在真正要測試的代碼前編寫 —— 由此有了測試優先開發 的概念。