一枚程序員眼中的單元測試
為了更好地閱讀體驗,歡迎訪問博客原文
論測試的重要性
如今程序員群體趕上了中國最龐大的農民群體,大街上隨便抓一把,十有八九是程序員,還一個剛從某國企離職報名參加軟件培訓班。我想碼農的稱號或許就是這么來的吧。
在外行人看來,程序員是一個成天對著電腦倒騰著代碼、看著Terminal上行云流水般的打印、過著不修邊幅的日子外加超負荷的碼農。
在內行人看來,程序員是一個成天面對QA的"質疑"、PM的"奪命催"以及DEVs的"吐槽",扛著身心壓力的苦行僧。
在我看來,程序員應該是:
手持神劍,心懷善念,胸有成竹、有理有據并且合情合理地跟QA、PM、DEV斗智斗勇的戰士。
要擺脫QA的質疑、DEVs的吐槽以及PM的奪命催,除了那些不容易掌控的客觀因素,我們可以從自身發力,加強自身的"核心肌群",呈現出自己的應有的專業態度,編寫出高質量的代碼,從而促成高質量的交付。
如何交付高質量的代碼?
首先,我們可以擺出苦行僧的心態,平日里練就一身好把式:如Clean Code、Refactor、OOD及FOP。即便這樣,牛逼哄哄的程序員也不敢說自己的代碼百分之百沒有缺陷。
怎么辦,兩個參考原則:
編寫完代碼多問自己一句:"真的可靠地完成目標了嗎?" 怎么問,寫個測試來提問。這便是?測試覆蓋。
編寫代碼之前先問自己一句:"怎么樣才算完成目標了呢?" 怎么問,同樣寫個測試來提問。這便是?TDD + 測試覆蓋。
測試能做什么
要知道測試能做什么,首先我們需要知道測試是什么(它在測什么)?它能給我們帶來什么價值?以及人力成本那么昂貴,我們為什么還要花時間去編寫這些上不了產品的測試代碼?
程序員總喜歡倒騰點代碼來開始一個話題:
這一小段測試代碼所做的事情是在驗證StringUtils#toUpperCase方法的功能正確性。
順便用一句話來形容單元測試:
開發人員編寫一小段代碼,用于檢驗被檢測代碼的一個很小的、很明確的功能是否正確。
廣義上的測試并不總是像上面這段代碼這么簡單,熟為人知的?測試金字塔?將測試分為三大類,單元測試位于測試金字塔底端,旨在傳達單元測試應該來得更兇猛一些,而它們正是由開發人員親手編寫出來。本文也是圍繞單元測試來開展。
測試的價值何在
經常聽開發人員說:"我對我的代碼非常有信心。"理由往往充分且單一:單元測試是老大,老大罩著我不怕。(當然,專業的QA始終能發現DEV很難察覺到的Defect,難免會驚起一臉狐疑:老大不靈了嗎!回首代碼,覺漏某一Case)。
所以單元測試能夠增強你寫代碼的信心。都說自信是成功者必不可少的特質。當你對代碼充滿信心之后,你的潛能無形中被激發(你會發現你敲代碼的速度都會變快),這樣你工作效率的提高促使你更加輕松地完成工作。身心受益便會產生一連串良性的"蝴蝶效應"。
測試的兩個無形價值就體現出來了:
1.?增強我們寫代碼的信心。 2.?讓我們更加輕松的完成工作,身心收益。
再來說說有形的代碼。缺陷減少了則證明你的代碼質量提高了,代碼質量衡量指標總離不開可讀性、可擴展性、可維護性。這三個指標的增強反映了良好的代碼整潔度、OO設計、模塊化等。實踐證明,這些良好的設計往往不是一蹴而就的,而當你為一個類或方法編寫單元測試卻舉步維艱的時候,你就應該考慮去改良你的設計了。
理想情況下,編寫完的代碼應該是可以工作的。但現實并不那么美好,當你在驗證代碼正確性的時候遇到問題,你就不得不頻繁地啟用調式模式,而調試正是吞噬你寶貴時間的惡魔。此時我們要拔出單元測試這把神劍,使出渾身解數將惡魔驅趕到塵封的黑暗角落,從而縮減我們花在調式上的時間。
那么,測試的兩個有形的價值也體現出來了:
3.?改良我們的設計。 4.?縮減我們花在調式上的時間。
在敏捷開發領域,文檔(需求文檔,詳細設計文檔等)是罕見之物。當一個新人半途加入項目的時候,在沒有太多文檔的情況下,閱讀測試代碼便是一個很好的開始。當然,前提是我們的測試代碼必須是可靠的,并且具有良好的可讀性。單元測試的第五項不可小覷的價值就被體現出來:
5.?測試即文檔。
不寫測試又如何
有一種聲音:"單元測試代碼寫得再漂亮,也終究不是產品代碼,在部署到生產環境時會被無情的拋棄掉!"
所以被這種聲音迷惑的人開始信奉了長(測)話(試)短(少)說(寫),短(甚)話(至)不(不)說(寫)的信仰。這只是經過修飾得以傳播的一種聲音,而背后做支撐的總有那么幾大派系。
無辜派
1.?我并不清楚代碼的行為,你叫我怎么測試呢?2.?這些代碼命名都能夠通過編譯啊!
個性派
1.?測試代碼不是我的工作,這不應該由專門的人去做嗎?2.?公司請我來是為了寫代碼,而不是寫測試的。
同理派
1.?如果我讓QA人員沒有工作,那么我會覺得很內疚的!
仔細推敲這三大派系,甩出幾個問題就能讓這些借口不攻自破:
1.?如果連代碼的行為都不清楚,寫出來的代碼意義何在? 2.?通過編譯就代表能正常工作嗎? 3.?你可以不寫測試,但你寫的代碼不斷被QA找出Defect,作為DEV名聲信譽何在,難道寫出可靠的代碼也不是你的職責嗎? 4.?公司的確不是雇你來寫測試的,那公司是顧你來調試bug的嗎? 5.?試問QA會喜歡一個交付的代碼存在很多Defect的DEV嗎?我想QA也寧愿代碼可靠到讓他ta"無事可做",從而去做一些功能測試、性能測試、驗收測試等。
讓我覺得值得一提的是常規派的看法:
1.?編寫單元測試太花時間了,項目結束時再說吧!2.?運行測試時間太長了!
"編寫單元測試太花時間了,等測試結束后再說"?聽起來是一個很合乎情理的想法。而在軟件開發項目上存在一個這樣的魔咒:
一推再推的事情,往往都是不會去做的事情。
不去做的原因可能是重視度不夠,被和諧掉了,也可能是最后想去做也沒有時間去做。不管出于什么原因,不寫測試存在潛在的風險。
實踐證明,隨著時間推移,產品的功能性的變化趨勢受測試代碼編寫的時機的影響如下圖所示:
好想法抵擋不住現實的打擊,代碼庫隨著項目的進展越發復雜,由于沒有測試的保護,一些不良的設計偷偷溜了進來,代碼越發嬌氣,慢慢地沒有人敢去動它。最糟糕的結果可能是,DEVs頂著巨大交付壓力,唯唯諾諾的寫著代碼,而災難正在醞釀,交付最終失敗。
所以只有當測試代碼并行于產品代碼,甚至可以采用TDD。測試的幾大價值才有可能被體現出來,從而能夠為我們的產品保駕護航。
另外,如果是因為不熟練而導致編寫測試的時間太長,不妨記錄一下自己每天花在定位問題和調試上的時間,做個對比,你會發現編寫單元測試最終是會為你節省時間的。
就我個人經驗,半TDD的編碼方式,在一個Story上所花的總時間不會多余沒有測試裸奔的代碼。或許剛開始會覺得有點拖慢節奏,操練多了,它的威力就會彰顯出來了。
測試也寫了,可是運行時間太長了又帶來了另一個苦惱?
細談該苦惱可以單獨寫一篇文章了。我的確見過測試運行時間很長,每次驗證一次跑上半個多小時。下面列舉一些測試加速的實踐:
1.?編寫更多的單元代碼來代替一些不重要的集成測試和UI測試。 2.?使用Mockito、JMock等工具模擬掉依賴。 3.?并行運行測試,前提是讓測試之間保持相互獨立。 4.?讓CI服務器去跑更耗時的集成測試和UI測試。 5.?使用契約測試來代替微服務之間的集成測試。
單元測試運行時間是毫秒級別的,如果耗時過長,你就要留意是否存在內存泄漏、資源未釋放、依賴過重或者不依賴容器而啟動了容器的單元測試。
揮之不去的例外
編寫單元測試是一項成本低卻價值很高的活動。編寫它不會花掉你太多的時間,而運行它更是毫秒間的事情。極限編程推崇者正在使用TDD的方式詮釋著單元測試的價值和意義。
它能帶給我們信心,改良我們的代碼設計,提升我們(DEVs)的聲譽,為代碼庫保駕護航,為高質量的軟件交付提供保障。但它終究不是一顆銀彈。我們編寫單元測試也無非是一種價值的取舍,當它給我們帶來的價值低于我們付出的成本時,我們就要保持警惕了,比如思考以下兩個問題:
1.?在追求漂亮的測試覆蓋率數字100%的時候,思考一下它真有那么高的價值嗎? 2.?在做快速的技術Spike(技術調研),思考一下不寫測試是不是能讓我更快的試錯?
我們要理解的是單元測試背后的核心價值,從而做出正確的取舍。我們要做的是編寫出有效的單元測試,讓它真正地為我們創造價值。
注釋
Terminal:命令行終端
QA:專職測試人員
PM:項目經理
DEV:開發人員,DEVs表示復數
OOD:面向對象設計
FOP:函數時編程
TDD:測試驅動開發
CI:持續集成
本文轉載自異步社區
軟件開發 編程語言
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。