從爬⾏到奔跑 - 我們為什麼需要單元測試?
來源:阿里雲開發者
阿里妹導讀
前⾔
什麼是單元測試
測試體系的演進
為什麼需要單元測試
測試⾦字塔
圖⽚來源:Software Engineering at Google
優秀的軟體離不開單元測試
測試更快:單測沒有其他外部依賴,跑的快,可以提供更快的反饋環,更快的發現並修復問題。
測試更穩定:同樣因為0依賴,單測相⽐於其他型別的測試更穩定,不會受外部其他模組的不相容變更影響。因此單測也是最能帶給開發者信⼼的測試型別。
問題更容易定位:單測以最⼩軟體單位為邊界,出了問題可以縮⼩定位範圍。相⽐之下,越是⾦字塔上層的測試型別,定位問題的困難度越⼤。複雜的端到端測試涉及眾多的模組,需要⼀⼀排查定位問題。
提升程式碼質量
好的程式碼是易測的:業界很早就提出了圈複雜度(Cyclomatic complexity)的概念,⽤來衡量⼀個模組判定結構的複雜程度,其數量上表現為獨⽴路徑的條數,也可理解為覆蓋所有的可能情況最少使⽤的測試⽤例個數。圈複雜度⼤說明程式程式碼的判斷邏輯複雜,可能質量低,且難於測試和維護。因此好的程式碼⼀定是圈複雜度低的,也是易於測試的。
易於迭代演進:沒有什麼軟體是⼀成不變的,好的軟體系統應該是易於演進的。單測覆蓋⾼的項⽬模組更原⼦化,邊界更清晰,修改起來更容易。單測覆蓋更全的項⽬重構的⻛險也相對更⼩,相反⼀個沒有單測覆蓋的複雜項⽬是沒⼈敢碰的。
更優質的設計:前⾯也提到,好的單測能夠提升程式碼的質量。如果⼀個研發需要給⾃⼰的程式碼寫單測,他就會注重程式碼的模組化分割,減少過⻓、圈複雜度過⾼的method。下⾯的例⼦就是⼀段沒有單測的程式碼的認知複雜度值(可以理解是圈複雜度的⼀個改良版,從程式碼是否容易理解的⻆度衡量),超標了⾜⾜三倍。現在回過頭來想補單測,腦袋都⼤。
減少debug時間:上⾯提到種種提升debug效率的原因,這⾥不再重複。⼀⽅⾯更⾼的單測覆蓋可以節省debug所花費的時間,另⼀⽅⾯有充⾜測試覆蓋的項⽬本身bug數量就會更少。舉個現實中的例⼦:某團隊由於歷史上⽋的種種債務,基本全靠端到端測試,毫⽆單元測試覆蓋。造成的後果也⾮常嚴重,團隊oncall的同學 > 50%的時間都是在修復各種奇怪的bug,沒法投⼊寶貴的精⼒到架構升級等⻓期更重要的項⽬上。
增加程式碼變更的信⼼:前⾯提到沒有測試覆蓋的程式碼沒⼈敢碰,有充⾜單測覆蓋的程式碼可以顯著提升改造程式碼的信⼼和意願。再給⼤家舉個例⼦:我加⼊阿⾥之前在Google總部⼯作過將近⼗年。如果你在Google⼯作過就會發現,你的程式碼經常會收到毫不相關團隊成員發起的code change。⼤多數情況下這些都是同學們⾃發的去做⼤⾯積重構(mass refactor),⽐如看你的Java程式碼沒有⽤Builder模式,就會幫你做個重構(Google⾥有⼤量⾃動化⼯具簡化這些重構⼯作)。我們拋開主觀意願不談,如果是沒有測試覆蓋的程式碼、還是毫不相關組的,你敢這麼重構嗎?我們都希望能有像⾕歌那樣整潔的程式碼,但沒⼈敢碰的程式碼怎麼變得更好?
提升程式碼⾃解釋性:⽂檔能夠提升程式碼的⾃解釋性,讓研發效率更⾼。好的單測其實也可以被看作程式碼的⽂檔,透過讀測試就能快速理解程式碼的作⽤(參⻅TDD)。單測作為⽂檔同時還完美的解決了⽂檔保鮮的難題,給開發者提供了⼀套⾼質量、隨著程式碼不斷更新的⽂檔。
更⾼效的code review:不是所有的問題和設計上的缺陷都能透過靜態檢查發現,這也是為什麼需要⼈⼯code review作為程式碼質量的最後⼀道防線。在Google,程式碼評審是程式碼合併最重要的⼀個環節,因此評審的效率直接影響總體的研發效率。好的單測覆蓋能夠減輕評審⼈的負擔,讓他們把精⼒投⼊到更重要的部分(⽐如程式碼設計)。
更頻繁的發版:敏捷開發倡導的持續整合、持續部署的前提就是全⾯、⾼質量的⾃動化測試。敏捷開發對於研發的提效就不多展開了。但光是能夠更快速的發版本身就已經⾮常有價值了。
反⾯模式和常⻅誤區
測試的反⾯模式(anti-pattern)
圖⽚來源:Software Engineering at Google
圖⽚來源:Software Engineering at Google
測試的常⻅誤區
總結
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70024924/viewspace-2985197/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 為什麼我們需要 VuexVue
- 我們為什麼需要CDP?
- 從AIGC到AGI,為什麼我們需要更多的“技術信仰派”?AIGC
- 為什麼我們從Yarn切換到pnpmYarnNPM
- 我們為什麼需要async/await ?AI
- 到底為什麼我們需要 Clickhouse?
- 我們為什麼需要雲原生?
- 為什麼我們從RabbitMQ切換到apache kafka?MQApacheKafka
- 為什麼我們從Webpack切換到Vite - ReplitWebVite
- 我們為什麼需要 lock 檔案
- [譯] 為什麼我們需要 Web 3.0Web
- 什麼是Web workers?為什麼我們需要他Web
- 我們為什麼需要API管理系統?API
- 為什麼我們需要訊息佇列?佇列
- 為什麼我們需要volatile關鍵字?
- 為什麼單元測試的目標從類改為依賴行為? - miro
- 從服務之間的呼叫來看 我們為什麼需要Dapr
- 進擊的WebRTC:我們為什麼需要它?Web
- golang拾遺:為什麼我們需要泛型Golang泛型
- 為什麼我們需要配置環境變數變數
- 為什麼我們需要資料庫事務資料庫
- 為什麼我們需要更注重原始碼安全?原始碼
- 測試夜點心:單元測試測什麼
- 單元測試效率優化:為什麼要對程式進行測試?測試有什麼好處?優化
- 為什麼我們需要服務網格Service mesh?
- 我們為什麼需要 DevSecOps 和製品倉庫?dev
- 爬蟲為什麼需要HTTP?爬蟲HTTP
- 提起模糊測試時我們在說什麼
- 為什麼我從 npm 到 yarn 再到 npm?NPMYarn
- 講道理,React中,我們為什麼需要寫 super(props)?React
- 為什麼我們需要Logstash,Fluentd等日誌攝取器?
- 我們為什麼需要模擬服務機器人?機器人
- 測試需要掌握演算法到什麼程度?演算法
- 孩子們,為什麼我建議你一定要會介面測試?
- [譯] 我們能從 Redux 原始碼中學到什麼?Redux原始碼
- 我們需要什麼樣的 ORM 框架ORM框架
- 為什麼單元測試不是持續交付的唯一答案
- 我們從爬取1000億個網頁中學到了什麼?網頁