為什麼很多程式設計師不喜歡寫單元測試?
業界良心。。。。
我認為這和懶惰與否沒有關係,測試不是必須要寫的,有人說:僱主不是為測試而付錢給我的(大意),這話的意思是如果客觀條件不允許(比如時間緊張)或者沒必要(比如目的碼你閉著眼睛都能寫得很好)等情況下,測試不寫也就不寫了,沒什麼大不了。
如果你覺得自己“懶得寫測試”,那麼你必然有“犯懶”的理由吧?時間緊或沒必要,你佔了哪一條?
如果哪條都不沾,那或許是你不懂如何寫測試——我指的不是技術層面上的“懂”,而是說如何讓測試行之有效又不過度耗費你的精力——這就涉及到測試能帶給我們些什麼好處了。
對於程式設計師個體而言,測試最大的意義在於“建立和維持對程式碼的自信心”,這一點大致可分為兩個層面:
-
從無到有的時候。如果你對要寫的程式碼無比熟練,揮手即來,那麼測試對你的意義微乎其微(此處假定程式碼的編寫和維護都是你一個人,不牽扯協作開發者。並且就算有牽扯,測試的必要性也要視乎你們的協作方式,這個後面再說)。可如果你對要寫的東西很陌生,腦袋裡一團漿糊的時候,測試就可以變成你的“引路人”。簡單地說,就是把複雜的大問題拆分成簡單的小問題,然後用測試描述每一個小問題的輸入輸出,接著失敗 => 程式碼實現 => 成功,如此迴圈往復,最終完成了整個解決方案。
顯然,不寫測試也可以這麼做(指拆分複雜問題的方法論),但是測試會有一些好處:- 每一個測試只對一個問題單元負責,哪裡不通過哪裡有問題,這有助於把複雜邏輯解構成若干簡單的模組化的程式碼。等到重構的時候你就知道這有什麼好處了。沒有構造良好的測試,沒人願意重構。這對於程式碼的維護是很有幫助的,除非你是一錘子專案,那就無所謂了。
- 因為測試先於程式碼編寫(其實就是 TDD),所以對於介面的設計往往是先於內部實現完成的。這是一種很好地程式碼設計習慣,然而雖然人人都知道但大多數人總是做不到。測試則會間接的“逼迫”你先考慮介面設計,再關注具體實現,若你的介面設計有問題,通過先寫測試很容易就能察覺到,避免寫一大堆程式碼之後才發現這麼寫不合適。
-
從有到優的時候。剛才提到重構,除了維護程式碼需要重構,修補 Bug 或是增加新的業務邏輯也算是重構的一部分(不太嚴格的說)。測試本身就是對業務邏輯的例項化描述,你的測試能否覆蓋到業務實際的範圍取決於你對業務本身的瞭解程度有多深,不過反過來就算你有遺漏或者理解上的偏差也沒關係,因為那些正確的測試已經保證了你實現的正確的部分。接下來對於錯誤(bug)你只需要修正有問題的測試或者補充沒有覆蓋到的邏輯;而對於新增加的業務也是一樣的道理,擴充測試用例即可。當然最後一步是讓都 pass。如果你測試寫得好——我是說語義良好,那麼它幾乎就可以拿來做需求文件了。回頭 PM 說你那兒那兒做得不對,你可以搬出測試給他/她逐條講解,很容易就能知道你們之間的誤差在哪裡。
除此之外就是針對程式碼質量的重構了,沒有測試的時候最頭疼的問題就是不敢隨便對程式碼動刀子,因為你沒有可靠的質量保障,有了測試就好多了(不能保障百分之百不會出岔子,畢竟測試也是人寫的)。這方面就懶得多說了,自己經歷幾次就能體會。
總而言之一句話,對於個體而言,測試就是你對業務邏輯的理解在“紙面”上的體現,另外它可以促使你提高程式碼的質量,提高程式碼設計的水準。不過總的來說還是“錦上添花”而不是“雪中送炭”,不寫測試的專案多了去了,也不見得就都會死掉。寫不寫測試,一則視客觀條件,二則視主觀要求,我的態度是不強求。至於那些說寫測試降低效率的,純屬扯淡。我就這麼說吧:
如果你覺得寫了測試對自己沒有什麼幫助反而降低了開發效率,那你就別寫了——因為你不會寫測試(還是的,非技術層面的“不會”,而是理解層面的)。
再把眼界放寬些,如果你不只是一個個體,你或許維護著一個全世界很多人都在使用(或將來可能會使用)的開源專案,那麼我的建議是:必須寫測試!
這種情況下,測試不只是寫給你自己看的,甚至都不只是寫給你的團隊成員看的,它更大的意義是寫給“外人”看或者用的。有人說“我們會出文件啊”,nonono,文件是給“消費者”看的,作為一個開源專案,你總是有兩類“客戶”。一類是拿來用的消費者,另一類則是改進改進讓它變得更好的貢獻者;後者有很多都是從前者演變過來的。
當你作為一個貢獻者參與進來的時候文件對你沒多大幫助,因為你的目標不只是會用,你還要知道它到底是怎麼工作的,要如何才能改動它以適應更多的要求。這個時候測試就像指南針,撰寫良好的測試就好像一個對專案本身瞭如指掌的大管家,一步一步手把手的指導你這個專案是怎樣從無到有一直髮展到今天的。沒有測試?那你就抱著原始碼一行一行摳去吧。而且事情往往是這樣的:測試良好的專案,其程式碼本身也很清晰,乾淨,易於閱讀和理解;反之,測試糟糕甚或是沒有測試的專案,程式碼往往如同一團亂麻一般,除了作者自己任誰看了都會頭大如鬥——哦,對了,就算是原作者,放上三個月不碰回過頭來還能認出自己寫得啥不?
你可以不寫測試,只要你理解了我上面所說的種種情況並能夠做出合適的決定,但是不寫測試絕對不應該和“懶惰”劃上等號。恰恰相反,有的時候我接到一個需求,我還就是“懶得”想實現的細節,索性先從寫測試開始一步一步抽絲剝繭,最後不知不覺的也就寫完了。這其實反映了測試恰恰是為“懶人”設計的編寫程式碼的方法,前提是你知道如何正確的編寫的測試。
現在大部分的測試教程都有一個通病,那就是用了大量的無意義的測試來舉例講解。這麼做可以理解,畢竟教程的作者又不知道你的實際情況,為了淺顯易懂起見,自然還是那些無意義的測試更容易看明白。我得承認真正學會寫測試是需要時間來練習的,有些東西真是隻可意會不可言傳。對於初涉測試的程式設計師來說,那些教程裡的無意義的例子的確容易給人一種錯覺:如果實際工作中也要這麼寫測試的話實在是太囉嗦太繁瑣了!沒錯,一開始是這樣的。一你得堅持練習,二你得找對方法;測試是為“聰明的懶人”準備的玩具。
還有一個是來自於現實世界中一個非常有名的例子,一群人利用測試和重構挽救了一個差點不可救藥的經典專案 Redmine,之後他們為這次大規模的重構寫了一本書:Refactoring Redmine,滿滿的都是經驗值啊。