說起單元測試的好處相信大家都能列舉出不少,可是很多時候,開發人員面對自己產品的代碼,想寫單元測試卻無從下手,久而久之,便會有人大喊:“我討厭單元測試。”資深敏捷咨詢師騰振宇(Daniel Teng)在GTUG-TopGeek開發工程管理沙龍就以此為題,結合最近的一個項目,和大家分享了他對單元測試的一些看法。
Daniel先介紹了下最近的一個項目,雖然不是遺留系統,但代碼已經慘不忍睹,而且缺乏必要的測試保障,要修改代碼可謂舉步維艱。例如,一段代碼和結對伙伴讀了半小時沒讀懂,找來原作者看著注釋又想了10分鐘,終于才搞明白這段代碼是做什么的。根據二八原則,先找到那20%的點,修改它帶來80%的價值。最直接的做法就是尋找代碼庫里最常被修改的文件,一般的文件只有幾次修改,有的文件則被修改了幾十次,每個人都在往復雜的代碼里加入新的東西,但沒有人往里面加測試,于是第一步就是為它增加測試。
很多開發者會說老項目就算了,如果新啟動一個項目,我就會寫單元測試了,Daniel認為這是一個“美好的夢想”,很多原因會打破它:
代碼已經很爛了,又沒辦法下手了
UI不好測
認為這是QA的工作
寫的單元測試找不到Bug
代碼的外部依賴太多
代碼稍作修改,測試也要一并修改,太麻煩了
究其根本原因,是開發者根本不會寫單元測試!滿足什么標準的測試才是單元測試呢?根據《修改代碼的藝術》,需要訪問數據庫的測試不是單元測試,需要訪問網絡的測試不是單元測試,需要訪問文件系統的測試不是單元測試……
為了更方便地進行單元測試,業務代碼應避免以下情況:
存在太多條件邏輯
構造函數中做的事情太多
存在太多全局狀態
混雜了太多無關的邏輯
存在太多靜態方法
存在過多外部依賴
例如,在代碼中存在硬編碼,或者是直接創建了一個數據庫連接,這種做法都是比較危險的,因為在測試時沒有辦法“偷梁換柱”。
想要寫好單元測試,學會重構是很重要的,重構的過程類似于清理廚房,雖然和做飯沒太大關系,但可以讓您下次做飯更方便,心情更好??梢灾貥嫷牡胤桨?,在待測試類與其依賴之間增加一層Test Fixture;將創建邏輯與業務邏輯分開等等。重構可以采取以下策略:
編寫測試代碼建立基本的防護網。在單元測試和功能測試之間要有取舍,如果單元測試實施成本很高,可以先加功能測試。
通過增加中間層來打破依賴,不是為了去掉依賴,而是為了后續的修改以及測試的便利。
將第一步中編寫的功能測試換成單元測試。
最后,Daniel還為大家提了一些建議:
項目里的破窗要修好,別容忍別人加新的破窗,用測試將破窗保護起來。
當你離開一個地方的時候,要讓它比你來的時候更整潔干凈。(童子軍軍規)
不停地重構你的代碼,每次走一小步。
隨后有人提問,如何評估一個單元測試的質量,用代碼行覆蓋率是否可行。Daniel認為沒必要去追求代碼行覆蓋率,真正要覆蓋的是邏輯,而不是代碼行。通過結對編程可以減少低質量的單元測試,人都喜歡改變,但沒人喜歡被改變,不要強求結對編程,讓他看到好處,嘗到甜頭,吸引他來做結對編程,沒有人喜歡落后,比如50%的人在做結對了,剩下的人自然會想嘗試。
當被問及單元測試是否是必須的時候,Daniel回答這并不是必要的,而是需要進行綜合的衡量,比如你的競爭對手一周前推出了一個產品,你需要在一周內完成產品研發并上線,這時可以選擇寫或者不寫單元測試,對于沒有寫過單元測試的人,一開始是需要上手的成本的。
如果您也對單元測試的話題感興趣,不妨關注騰振宇的博客和微博,也可以觀看會議視頻。