其實,最開始我是不作單元測試的。想一想有那麼多的代碼,那麼多的邏輯,要寫多少單元測試函數。真正能實現百分百(更百分之九十)的代碼覆蓋率嗎?想一想就不可能,那得寫多少代碼啊!有用嗎?並且若是代碼重構,之前的單元測試函數豈不廢了?java
讓我第一次認真思考單元測試,是有一次,軟件經過了功能測試,本身又想重構代碼的時候。c++
如何保證代碼重構後,不引入新問題呢?這時候想到了單元測試。web
要是某個模塊的接口(注意:本文中的接口是指外部可訪問的函數或變量-變量很是不推薦,而不是c#和java中的interface)是明確的,又有單元測試作保證,不就能夠放心重構模塊內部的代碼了嗎?數據庫
這時,我意識到,接口很重要,有好的接口,代碼就有了邊界,在接口不便的前提下,邊界內的代碼能夠隨意動。c#
也認識到,單元測試可能沒有想象的那麼多。緩存
若是接口是內部接口(不做爲庫使用),那麼只要調用的地方覆蓋到了也就好了。框架
我還意識到,單元測試不能注重代碼覆蓋率,而應該注重邏輯覆蓋率或功能覆蓋率。函數
寫單元測試不能深刻到代碼中,而是應該站在接口的角度,考慮有多少種調用狀況。從這個角度看,單元測試應該是黑盒測試。有多少人被代碼覆蓋率弄得暈頭轉向,可嘆!單元測試
若是代碼習慣比較好,沒有無效代碼,覆蓋了完整了邏輯,代碼也就基本覆蓋了。因此整潔的代碼,走心的編碼是必不可少的。學習
後來我瞭解到,不少人認識單元測試,都是從支撐重構角度去思考的。因此說,重構是很重要的,至少它會引領你去思考單元測試。
這裏有個誤區,若是不認真下手寫單元測試是認識不到。那就是不少人認爲能夠先編碼,編碼完成後,甚至到重構時再寫單元測試也能夠。持這樣觀點的人,應該沒有真真正正寫過單元測試,或認真思考過單元測試。
其實,這裏也有另外一個引人思考的問題:咱們應該何時重構代碼?顯然過了功能測試後再重構代碼,必定意義上來講,有點晚。
我還感受到,單元測試可能會下降咱們們的開發時間.
拿web測試爲例(此處不是說頁面修改),咱們在修改了代碼,須要編譯,發佈,打開網頁去驗證咱們的修改,有時還須要登陸,打到位置再測試,若是頁面有緩存,則要處理緩存。這多麻煩,容易出錯,用時還長,不少仍是重複的步驟。若是有單元測試,按一個鍵就搞定了(這是我當時的想象)。
在認識到單測試有用,測試函數可能沒有想象中多的時候,我就開始有意去作單元測試。
在剛開始參加工做時,曾用junit寫過一些單元測試.
那時對單元測試現解不深,並且作的是對日外包項目。該項目刻板到每一個函數都要寫僞代碼,單元測試能寫成什麼樣子可想而知了。因此此次基本沒獲得多少有益的經驗。
因爲我在用c++,不知道還有gtest,nggtest框架,也許那時還沒出來,或者名氣還不大,再或者我找東西的方式有問題,反正我沒找到一個合適的框架。我爲此浪費了很多時間。其實,咱們徹底不須要執著於什麼框架。這也顯示了當時我不知道單元測試怎麼作。
及至後來看到一個開源項目live555(或者是其它項目,記不清了)。它的單元測試就很是簡單,直接printf出結果,也沒有自動對比正確性。我突然意識到,什麼形式框架根本不重要,重要的是咱們想要的是什麼。
因而,我開始不用框架,本身寫一些簡單的單元測試程序。因爲單元測試函數共同點太多,例如初始化,判斷,統一執行等,很快我演化出了本身的測試框架,這裏再也不詳述。
最開始寫單元測試時,寫的並很差。主要是分層分的很差。常常一個測試跨多個層,甚至連通信軟件也用上了,例如ActiveMQ。
一度致使我苦惱好久:通信層怎麼測,數據庫交互怎麼測?
後來纔想明白:ActiveMQ和數據庫與測試有什麼相關,都是第三方的東西,去測第三方東西幹嗎,就當成它們都是正確的就好了。
咱們主要目標測自已的邏輯是否正確,至於數據從哪來,到哪去,只要數據正確,能夠默認都是好的。
因爲當時不知道mock,在這一個塊花的工夫還真很多,例如本身從新寫一個被調用的邊界函數,裏面提供數據,或對數據進行判斷。
一直覺得寫本身寫代碼很謹慎,對待接口函數很重視,寫了單元測試後,才發覺作的也仍是不夠。
記得有一次寫服務程序,週期運行。在函數中用到了系統時間,後來發現很差測。由於咱們沒法控制系統時間,因此每次要等一陣才能運行一次測試,很不方便。
這使我意識到這裏對系統有了依賴,應該去除這個依賴,因而就將系統時間做爲接口參數傳進去,測試就方便多了。能夠傳入想測的時間,進行測試。後來瞭解到mock,才知道能夠mock系統時間,但我總以爲去除系統耦合性是頗有必要的。
一直在思考,何時作重構最合適,功能測試完成後,顯然不是適合的時機。由於一改動,可能形成功能測試白測了。
我一度認爲,在完成幾個類時,回頭看看有沒有共同點,若是有,則需重構。這時是個合適的時間點。又認爲一個類或源文件完成後,適合重構,修改一下命名,添加一下注釋,拆分一下長函數。
寫單元測試後才發現,重構適合在一個對外接口函數完成後,作單元測試的時候。 這時候,能夠經過單元測試驗證接口設計是否合理,邏輯是否正確,分層是否合適。
何時開始寫單元測試好呢?
TDD說寫代碼以前先寫單元測試,我沒有嘗試過。
在編碼時, 我不想讓單元測試打段我編碼時連貫的思路,因此我選在第一個對外接口函數完成時。
固然,也能夠選在想編譯一下,看看是否能編譯經過的時候。
可是千萬別選在全部接口函數都寫完後,由於這時極可能你的代碼因爲耦合度太大或分層很差,已不具有可測性;還可能因爲感受測試代碼量太大,放棄寫單元測試。
這裏就說到測試代碼量,不少人不寫單元測試,就是以爲單元測試代碼量太大,性價比不高。
其實真正寫單元測試就會發現,單元測試量不大,一個接口函數最多也就五六個測試用例,再多就須要思考函數設計、定位是否合理了。
個人實踐狀況是:單元測試用例就四五個,睛着函數看半天,需不須要更多的測試函數呢?結果發現,沒有,就這些。我常常有這種體驗。
這些測試函數通常不會太長,同一接口函數的測試函數應該比較相似。
不少人覺很寫單元測試很耗時,會浪費大量時間,其實否則。
第一, 單元測試的同時,把重構給作了,節省了部分重構的時間。
第二, 單元測試早編譯,早發現問題,處理問題也容易。
第三, 單元測試利於調試,咱們寫代碼,大半時間在調試,調試時搭建環境須要很多時間,再執行一些重複的步驟進行調試,有時候須要重複多遍。
而單元測試,只須要運行一下就能夠了。
第四, 還有,寫一個程序,天然但願快速調用一下,看看效果,這是一個比較天然的過程,單元測試正好能夠知足咱們這個需求。
單元測試並不難寫,我寫單元測試的思路是,寫最少的代碼,完成我想要測試的功能便可。感受寫單元測試就像寫一篇有思路時的做文,仍是很順暢的。
mock是我最近才瞭解到的技術,這個技術很是有用。
之前寫單元測試,遇到調用外部接口時,會本身實現一個版本,並在單元測試中以本身實現的版本替換真正的外部實現,有了mock後,咱們就不須要這樣作了,只須要簡單調用mock就好了,並且mock能夠作得更好,更多。
例如,有個定時器或線程,要是沒有mock,就很差測,除非將線程邏輯函數寫爲公用的,有了mock就方便多了。只是mock的語法稍微複雜一些,須要學習。另,用mock的測試用例可讀性會差一些。
我如今的習慣是,若是新功能,無論項目要求不要求寫單元測試,我本身都會寫單元測試,即便不會提交到代碼庫中。
不寫單元測試,就像爬山沒有柺棍,不是不行,可是總以爲不方便。
編碼完成後,我就對單元測試不怎麼關心了,這習慣很差。這也和本身最初認爲的「單元測試是爲重構保駕護」不一致。
我以爲單元測試真的在編碼階段起的做用比重構時要大,或者說在編碼時,我己進行了大部分的重構。
最後我想說的是,單元測試頗有用,但不是必須的。這只不過是一種比較好的實踐罷了。
不用爲此糾結。若是你想用,建議當即下手使用,多想多作,天然能體會到它的好處。
沒想通,沒興趣,不作單元測試也沒什麼,畢竟不少時候咱們不寫單元測試也這麼過來了。
只是於我我的每當遇到難解的bug,我會感嘆,當初要是有單元測試多好。