如何說服前端同學寫單測?

寫在前面

網上寫單測的文章太多了,要麼是介紹工具(jest、mocha、chai 等)的,要麼是照本宣科介紹什麼是單測的。看過之後總是覺得無法指導實際開發,不夠接地氣,於是嘗試着總結了此篇文檔,希望能夠補上這塊缺失。所以本篇文章裏不會出現單測的基本概念與單測工具的介紹,有的只是指導實踐的一些方法論。

爲什麼要求單測覆蓋率?

首先,「覆蓋率」不是目的,只是達到目的的手段,我們希望:

  • 通過「單測」喚起同學們對代碼質量的意識;
  • 通過「單側覆蓋率」這個指標來保證和推進方案的落地;
  • 通過「寫單測」來提高同學們的代碼質量(爲什麼能提高?請看下面「重新認識單測」部分);

所以,「覆蓋率」絕不是目的,雖然覆蓋率不能簡單的等同於代碼質量,但是如果想持續提高覆蓋率,並把覆蓋率維持在一個較高水平,是一定會觸及到更深層次的改變的。
至於如何制定覆蓋率的標準,這個建議由項目自己制定一個長期計劃,此計劃務必做到持續提高維持高水平,可參考下文「怎麼寫好單測」部分。

什麼不是單測?

比起什麼是單測,讓我們先來看看什麼不是單測,也許會給我們更多啓發:

需要訪問數據庫的測試不是單元測試
需要訪問網絡的測試不是單元測試
需要訪問文件系統的測試不是單元測試
— 修改代碼的藝術

純函數,副作用,冪等,函數式編程……如果你的腦海裏出現了這些詞彙,恭喜你,你已經上道了。

寫單測有什麼好處?

  1. 安全放心:這是顯而易見的,相信你一定有過不少上線時心裏沒譜甚至膽戰心驚的經歷吧。
  2. 大膽重構:就算這個系統是你從零搭建的,你重構的時候也絕對會犯同樣的錯誤。
  3. 解釋性:測試用例就是你最好的說明文檔,甚至相當於 demo。

以上是直觀的,大家都懂的,下面說些容易被忽視的

  1. 提升設計能力:如果你認真對待單測,那麼它會強迫你寫出可維護性更好的代碼。如果你發現代碼不容易甚至不能寫單測,那麼八成是代碼設計的問題。當你思考並重構了這部分代碼時,相信你會覺得付出的精力和時間都是值得的,你再寫出「可測試性不好」的代碼的可能性就越來越小了。另外,據說 85% 的缺陷都在代碼設計階段產生,而發現bug的階段越靠後,耗費成本就越高,指數級別的增高。
  2. 提升全面思考能力:單測經常會寫邊界 case,久而久之這種思路會形成慣性,以後在寫代碼時思路會非常清晰,思維縝密、面面俱到也會成爲習慣。
  3. 提升代碼質量:這步已經是水到渠成的了,是上面幾條所結的果,無需贅述。
  4. 節省時間:別噴我,我只是個搬運工,請看從頭到腳說單測——談有效的單元測試裏提到的《單元測試的藝術》那個例子

什麼是好的單測?

  1. 正確、清晰、簡潔。實踐中單元測試不光測試代碼的正確性,還能夠幫助其他開發者理解代碼邏輯,理解如何使⽤相關的類或者函數(可以當做接⼝或函數的使⽤⽰例了,省的寫使⽤⽂檔和 demo 了),所以要求單測寫的清晰,簡潔,有⾮常好的可讀性。
  2. 完整性,也是必需的,單測應該有很⾼的覆蓋率,把可能的輸⼊輸出場景都考慮到。
  3. 健壯性,是最容易被忽略的一項。當被測試的類或者函數被修改內部實現或者添加功能時,⼀個好的單測應該完全不需要被修改或者只有極少的修改。⽐如⼀個排序函數的單測實現是完全穩定的,它不應該跟着不同的排序算法⽽變化。
  4. 有個好名字,讓⼈⼀看就知道是做什麼測試,如果名字不能說明問題也要加上完整的註釋。⽐如 testSortNumbers_withDuplicated, 意味SortNumbers函數的單元測試來驗證有重複數字的情況。
  5. 邏輯簡單,儘量避免使⽤命令式編程(Imperative Programming)引⼊條件判斷,循環等複雜邏輯。否則很可能會給單元測試⾃⾝帶來不少bugs,這樣就需要寫單元測試的單元測試了……⼀句話單元測試不要引⼊複雜的邏輯,最好是不要引⼊邏輯。
  6. 完備⽽不重複。同樣的測試場景,或者同類型的測試輸⼊不要寫多個單元測試,找⼀個有代表性的場景輸⼊就可以了。

怎麼寫好單測?

以下只是一些理論,請結合實際進行改良

戰略四步走

  1. 會寫,項目裏有工程,可以寫單測,人人可寫即可
  2. 寫好,對代碼是否可測有判斷力,知道如何寫出「可測試性好」的代碼
  3. 重構,隨着單測覆蓋率的提升,一些代碼自然被重構,開始出現良性循環
  4. 常態,寫新代碼時,已經想好了單測怎麼寫,二者同時進行,形成常態

戰術四要素

  1. 核心代碼優先、重點覆蓋單測,如公共函數,公共組件等。此時要求「精」,處於戰略 1、2 階段,要注意修煉內功
  2. 設計好目錄結構。如常量(無需測試)單獨一個文件,並統一命名,方便 ignore;UI 組件與數據處理等邏輯分爲不同文件,不要耦合;公共組件放在一個目錄下,方便統計覆蓋率等
  3. 函數式編程思維,盡最大可能降低函數的副作用,最典型的就是要把 window,localStorage 等全局變量作爲參數傳入,而不是在函數裏直接訪問並操作
  4. 把副作用控制在一定範圍內,副作用是很難避免的,那麼就把它們統一收斂起來,比如操作 localStorage,只允許通過一個二次封裝的函數來觸發,類似於 redux、vuex 等狀態管理機制的思想

參考資料

淺談前端單元測試
爲什麼談前端單元測試
前端單元測試實踐
從頭到腳說單測——談有效的單元測試
乾貨 | 測試扁平化之必備神器:好的單元測試

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章