摘要: 單元測試應該是程序員的必備技能,而真正的編程高手應該善於把握單元測試的粒度。javascript
在前一篇博客,我說起到了最近在對後端Node.js服務進行代碼重構,將Promise替換成Async/Await。這是一件痛並快樂着的事。html
當任務完成50%以後,我發現,與其說是重構,更準確的說法或許是重寫。一方面,換用Async/Await自己就意味着須要修改每一個異步函數,然後端絕大多數函數都是異步的;另外一方面,做爲一個有着強迫症的完美主義者,我寫了大量單元測試對代碼進行了一系列優化,同時修復了一些BUG,而且實現了一個新功能。前端
這裏的關鍵詞是單元測試,那麼問題來了,重構代碼就得了,寫什麼單元測試啊?這不是沒事找事麼,要知道單元測試彷佛比功能代碼更難寫。java
這是一個頗有意思的話題。node
在《玩轉Node.js單元測試》中,我是這樣定義單元測試的:程序員
所謂單元測試,就是對某個函數或者API進行正確性驗證。shell
這樣的定義很是通俗易懂,但並非很準確,嚴格來講應該是錯誤的。由於對API測試時,會涉及到多個函數,不少時候還會依賴於數據庫、緩存以及第三方服務等外部資源。所以,API測試應該屬於集成測試而非單元測試。數據庫
根據《JavaScript有這幾種測試分類》,集成測試與單元測試應該是這樣區分的:編程
單元測試指的是測試小的代碼塊,一般指的是獨立測試單個函數。若是某個測試依賴於一些外部資源,好比網絡或者數據庫,那它就不是單元測試。後端
集成測試就是測試應用中不一樣模塊如何集成,如何一塊兒工做,這和它的名字一致。集成測試與單元測試類似,可是它們也有很大的不一樣:單元測試是測試每一個獨立的模塊,而集成測試剛好相反。好比,當測試須要訪問數據庫的代碼時,單元測試不會真的去訪問數據庫,而集成測試則會。
所以,對於單元測試,更加準確的理解應該是對單個函數進行獨立測試。
可是,在實際操做中,測試單個函數時,很難保證所謂的獨立測試。一些函數不免依賴於其餘函數、數據庫、函數以及第三方服務等外部資源,這個咱們很難避免,甚至有時偏偏須要驗證這些外部資源。好比,驗證寫入數據庫或者緩存的數據是否符合預期;驗證數據庫或者緩存中的數據對函數行爲的影響是否符合預期。
在我看來,對單個函數進行非獨立的測試,不妨也能夠視做**「單元測試」。簡單地說,本文所討論的單元測試**,就是對單個函數進行測試。
新功能的增長,代碼複雜性的提升,優化代碼的須要,或新技術的出現都會致使重構代碼的需求。在沒有寫單元測試的狀況下,對代碼進行大規模修改,是一件不敢想象的事情,由於寫錯的機率實在太大了。
我一直在鼓勵你們寫單元測試,然而,有時不免偷懶。當我打算重構代碼的時候,發現寫的單元測試實際上是不夠的,這就比較尷尬了:(
那我究竟是直接改代碼;仍是先寫單元測試,而後再改代碼呢?這是一個艱難的決定,由於前者很難保證正確性,後者貌似須要耗費大量時間。
有一種智慧叫作「摸着石頭過河」:我嘗試在修改函數代碼以前,補寫一些單元測試。這個過程並無想象中那麼痛苦,也許是由於作決定自己其實比作事情更痛苦,或者是由於我比較喜歡敲代碼。
因而,我就能夠開始大刀闊斧地進行重構了:換用Async/Await;優化代碼組織;優化程序性能;寫新功能...忙得不亦樂乎。
若是沒寫單元測試,我敢怎麼作嗎?固然不敢!出錯了還得我來改啊。
若是沒寫單元測試,我會改得那麼快嗎?固然不會!大概每改一個函數都會想半天,改完而後祈禱它不會出錯;修改某個函數並非一蹴而就的事情,若是每次修改都去磨嘰半天,大概我如今還在敲代碼而不是在寫博客。
正是由於有了單元測試作保證,改起來纔會駕輕就熟,效率更高。這樣,既能夠保證正確性,又能夠節省時間。想象中單元測試會浪費很多時間,事實上彷佛並不是如此。
Fundebug是全棧JavaScript錯誤監控平臺,支持各類前端和後端框架,能夠幫助您第一時間發現BUG!
也許大多數人沒有我這麼喜歡折騰,不會一直去重構代碼,這種狀況下,難道就不用寫單元測試啦?
我想答案應該是否認的。由於單元測試有不少顯而易見的好處:
另外,單元測試可以提供另外一個思考代碼的角度,這對於編寫高質量的代碼是頗有好處的。
本文聊的單元測試是針對每個函數的,那麼,你在寫單元測試的時候,就會去考慮合理地拆分與合併函數。由於函數的功能區分不清楚的話,是不太好寫單元測試的。
敲代碼的時候,咱們考慮的是函數實現,無論三七二十一,寫好了就大功告成了。寫測試的時候,咱們跳出了函數,從輸入輸出的角度去思考函數的功能,這時候,你就會去想,這個函數真的須要嗎?這個函數的功能是否是能夠簡化一下?這個函數考慮的狀況彷佛不夠全面吧?這些思考,能夠幫助咱們寫出更好的代碼。
若是你是編程高手,彷佛能夠少寫一些單元測試。王垠大神在《測試的道理》中是這樣說的:
在我心目中,代碼自己的地位大大的高於測試。我不忽視測試,但我不會本末倒置,過度強調測試,我並不推崇測試驅動開發(TDD)。我知道該測試什麼,不應測試什麼,何時該寫測試,何時不應寫,何時應該推遲測試,何時徹底不須要測試。由於這個緣由,再加上高強的編程能力,我屢次完成別人認爲在短期不可能完成的任務,而且製造出質量很是高的代碼。
那麼問題來了,你是高手嗎?根據二八原理,大部分開發者並不是高手。在下自認爲編程水平還不錯,也選擇儘可能寫單元測試。
假設你是高手,那你能保證你的團隊都是高手嗎?根據二八原理,一個團隊裏面只有少數人是高手。若是你不寫足夠的單元測試,他們亂改你的代碼,是會出事情的。
因此說,仍是得儘可能寫單元測試,不管你是否是高手。
固然,你也不能沒完沒了地寫單元測試,不然就本末倒置了。
另外,單元測試寫得越多,其邊際收益是在不斷下降,是得不償失的。神奇的二八原理告訴咱們,20%的測試能夠覆蓋80%的問題;而剩下20%的問題,你須要寫80%的單元測試。換句話說,單元測試並不能消除全部問題。所以,對生產代碼進行實時錯誤監控是很是有必要的,這也是咱們Fundebug努力在作的事情。
在《單元測試要作多細?》中,耗子哥告訴咱們:
UT的粒度是多少,這個不重要,重要的是你會不會本身思考你的軟件應該怎麼作,怎麼測試。
這是每個程序員都應該認真思考的問題,沒有所謂的標準答案。從小接受中庸之道和惟物主義辯證法薰陶的咱們,應該能夠在實踐當中思考合適的測試粒度。當你學會了思考,你才能成爲真正的高手。
歡迎加入咱們fundebug技術交流3羣:490454644