1. 程式人生 > >單元測試系列一-為什麼要寫單元測試,何時寫,寫多細

單元測試系列一-為什麼要寫單元測試,何時寫,寫多細

一、前言

半年前在公司團隊內推動單元測試的落地,略有一些心得,記錄在此。如有不同觀點或建議,請在評論區留言,一起探討。

定義:單元測試是用來對一個模組、一個函式或者一個類來進行正確性檢驗的測試工作。

單元測試從長期來看,可以提高程式碼質量,減少維護成本,降低重構難度。但是從短期來看,加大了工作量,對於進度緊張的專案中的開發人員來說,可能會成為不少的負擔。

本文從為什麼要寫單元測試,何時寫,寫多細這些我們最關心的問題展開討論,試圖解答如何在進度緊張的專案中,通過寫有效的單元測試來提高程式碼質量的問題。

二、單元測試要寫多細

酷殼上有篇關於“單元測試要做多細”的文章,文章是從StackOverflow上的一個問題討論開的。這個問題是–

“TDD需要花時間寫測試,而我們一般多少會寫一些程式碼,而第一個測試是測試我的建構函式有沒有把這個類的變數都設定對了,這會不會太過分了?
那麼,我們寫單元測試的這個單元的粒度到底是什麼樣的?並且,是不是我們的測試測試得多了點?”

點贊最多的答案是–

老闆為我的程式碼付報酬,而不是測試,所以,我對此的價值觀是——測試越少越好,少到你對你的程式碼質量達到了某種自信(我覺得這種的自信標準應該要高於業內的標準,

當然,這種自信也可能是種自大)。如果我的編碼生涯中不會犯這種典型的錯誤(如:在建構函式中設了個錯誤的值),那我就不會測試它。我傾向於去對那些有意義的錯誤

做測試,所以,我對一些比較複雜的條件邏輯會異常地小心。當在一個團隊中,我會非常小心的測試那些會讓團隊容易出錯的程式碼。

這個答案給人感覺對單元測試持一定的否定態度和不感冒態度,但是這一最佳答案的回答者是Kent Beck!正是那位極限程式設計、測試驅動開發和單元測試以及JUnit的創造者Kent Beck。Kent Beck的答案,正好回答了單元測試要寫到什麼程度這個問題。單元測試不是越多越好,而是越有效越好!進一步解讀就是哪些程式碼需要有單元測試覆蓋:
1. 邏輯複雜的
2. 容易出錯的
3. 不易理解的,即使是自己過段時間也會遺忘的,看不懂自己的程式碼,單元測試程式碼有助於理解程式碼的功能和需求
4. 公共程式碼。比如自定義的所有http請求都會經過的攔截器;工具類等。
5. 核心業務程式碼。一個產品裡最核心最有業務價值的程式碼應該要有較高的單元測試覆蓋率。

三、何時寫單元測試

寫單元測試的時機不外乎三種情況:

(1)一是在具體實現程式碼之前,這是測試驅動開發(TDD)所提倡的;

(2)二是與具體實現程式碼同步進行。先寫少量功能程式碼,緊接著寫單元測試(重複這兩個過程,直到完成功能程式碼開發)。其實這種方案跟第一種已經很接近,基本上功能程式碼開發完,單元測試也差不多完成了。

(3)三是編寫完功能程式碼再寫單元測試。我的實踐經驗告訴我,事後編寫的單元測試“粒度”都比較粗。對同樣的功能程式碼,採取前兩種方案的結果可能是用10個“小”的單測來覆蓋,每個單測比較簡單易懂,可讀性可維護性都比較好(重構時單測的改動不大);而第三種方案寫的單測,往往是用1個“大”的單測來覆蓋,這個單測邏輯就比較複雜,因為它要測的東西很多,可讀性可維護性就比較差。

建議:我個人是比較推薦單元測試與具體實現程式碼同步進行這個方案的。只有對需求有一定的理解後才能知道什麼是程式碼的正確性,才能寫出有效的單元測試來驗證正確性,而能寫出一些功能程式碼則說明對需求有一定理解了。

四、為什麼要寫單元測試

這其實是問單元測試能帶來什麼好處。之所以把這個問題放在最後討論是因為如果一上來就說單元測試多好多好,終究還是會缺乏說服力。

而有了上面討論做鋪墊,我們更加清楚單元測試的使用場景,以及它做得到和做不到的。

單元測試的作用:

(1)讓我們對自己的程式碼有信心

修改了程式碼後單測依然通過的,起碼說明我們的修改沒有破壞程式的正確性。這從主觀上能增加我們對程式碼的信心。雖然單元測試通過了並不意味著程式就沒有bug了,但我們也要了解到這可能不是單元測試的問題。單元測試顧名思義是測試一個”單元”,這個”單元”一般是類或方法,而不是整個系統。對整個系統的測試那是整合測試,功能測試的職責。單元測試追求的是快速反饋,頻繁執行。整合測試雖然測“全域性”,但成本較高,所以執行頻率較少。兩者使用場景不同,目的不同。

2)為程式碼重構保駕護航

看到程式碼很差勁,想重構,但又擔心重構之後出問題,怎麼辦呢?如果有單元測試情況就不一樣了,重構完程式碼,跑一遍單元測試,如果單元測試都通過,基本上可以保證我們的重構沒有破壞原來程式碼邏輯的正確性。不過前提是之前的寫的單元測試質量很好,覆蓋率很高。當然這僅限於小範圍的重構,比如重構一個類或者函式的實現,但對於大刀闊斧的重構(比如單體重構成微服務,面向庫表模式重構成DDD),就不適用,那個時候要重寫單元測試了。

(3)通過單元測試快速熟悉程式碼

單元測試不僅起到了測試的作用,還是一種很好的“文件”,通過單元測試,我們不需要深入的閱讀程式碼,便能知道這段程式碼做什麼工作,有哪些特殊情況需要考慮,包含哪些業務。

五、小結

  • 何時寫
    單元測試與具體實現程式碼同時進行。

  • 寫多細
    哪些程式碼需要有單元測試覆蓋:
    1、邏輯複雜的
    2、容易出錯的
    3、不易理解的,即使是自己過段時間也會遺忘的,看不懂自己的程式碼,單元測試程式碼有助於理解程式碼的功能和需求
    4、公共程式碼。比如自定義的所有http請求都會經過的攔截器;工具類等。
    5、 核心業務程式碼。一個產品裡最核心最有業務價值的程式碼應該要有較高的單元測試覆蓋率。