Android單元測試(二):再來談談爲何

今天早上8點半坐到桌子前,打開電腦,看了幾分鐘體育新聞,作其餘一些準備工做,到9點開始真正開始着手寫這篇文章。因而開始google,找資料,打算列一大段堂而皇之的理由,來講明爲何要寫單元測試,好比:android

  • 對軟件質量的提高git

  • 方便重構程序員

  • 節約時間github

  • 提高代碼設計安全

  • 。。。網絡

等等等等。app

然而我發現上面提到的幾點,都不是很好解釋。首先,我並無具體的數據,來講明有了單元測試,咱們的app crash率降了多少,bug少了多少等等。這種東西首先咱們沒有去衡量,由於單元測試的增長是按部就班的,每一個版本的迭代增長一點點。很難,咱們也沒有,去先後對比。再次,crash率的下降和bug的減小,也難以證實就是單元測試的做用。另外,像重構這種理由,怎麼舉例證實呢?例子小了顯得沒有意義,例子大了寫起來很困難,讀起來也困難。而關於節約時間,我也沒有測量過,這個恐怕也很難去測量。只能從理論上去說明,爲何能夠節約時間,恐怕也很難有說服力的去論述。一樣的,對於代碼設計的提高,也很難有力的去證實。 框架

更重要的緣由是,上面提到的種種好處,好像其實並非我之因此要寫單元測試的直接緣由,更多的,他們像是一種結果。因此若是從列舉和證實單元測試的好處這個角度去說明爲何要寫單元測試的話,我感受甚至很難說服我本身。工具

那就從自身的經歷和感覺去說說,我爲何要寫單元測試吧。其實我之因此要寫單元測試,或者說這麼喜歡單元測試這種寫代碼的方式,是出於我自身的緣由,或者說由於自身的一些缺點,讓我走上了單元測試這條路,並且不再想回頭。單元測試

我爲何寫單元測試

首先,是由於我不夠自信

我相信你們都有接手,或者說參與到一個新項目的經歷,也許是由於換了工做,也許是由於職位調動,或其餘緣由。當我拿到一個新項目的時候,會有一種坐臥不安的感受,由於一時間比較難理清楚整個app的結構是怎麼劃分的,各部分各模塊之間又是什麼樣的關係。我怕我改了某一個地方,結果其餘一個莫名其妙的地方的受到了影響,而後致使了一個bug。這對於用戶羣大的app,尤爲嚴重。因此,那種時候就會但願,若是我改了某個地方,能有個東西告訴我,這個改動影響到哪些地方,這樣改是否是有問題的,會不會致使bug。雖然我能夠把app啓動起來,看看是否是能正常工做,然而一種case能工做,並不表明全部影響到的case都能工做。尤爲是在不知道有哪些地方用到了的狀況下,我更加難以去遍歷全部用到的地方,一個一個去驗證這個改動有沒有問題。哪怕我知道全部的case,這也是一個很痛苦很費時間的過程,並且不少的外部條件也很難知足,好比說須要什麼樣的網絡條件,須要用戶是會員等等。

在這種狀況下,單元測試是纔是最好的工具。首先,單元測試只是針對一個代碼單元寫的測試,保證一個代碼單元的正確性總比保證整個app的正確性容易吧?遍歷一個方法的全部參數和輸出狀況總比遍歷一個app的全部用戶場景容易吧?跑一次單元測試總比運行一次app快吧?

所以,在改現有的代碼以前,我會先對要改的代碼單元作好隔離,寫好測試,再去改,改好之後跑一邊單元測試,驗證他們依然是經過的,這時候我纔有信心,將代碼合併進去。

一樣的狀況會發生在重構的時候,我是一個對爛代碼不大有忍受能力的人,看到很差的代碼,我會忍不住想要去重構,否則的話,沒有辦法寫新的代碼。而重構就會有風險。由於我不夠自信,重構的時候,也會有一種坐臥不安的感受。這時候若是有完備的單元測試的話,我就能知道個人此次重構到底破壞了哪些地方,是否是對的,這樣相對來講,就會放心的多了。

所以,想用單元測試來保證代碼的正確性,這個是我喜歡寫單元測試的重要緣由之一。

再次,是由於我沒有耐心

對於有必定經驗,有必定代碼思想的人來講,當他拿到一個新的需求,他會先想一想代碼的結構,應該有那些類,那些組件,什麼責任應該劃分到哪裏去,而後纔開始動手寫代碼,這個是很天然的一個思惟過程。然而在不寫單元測試的狀況下,咱們可能要把整個feature都作完整,從model到controller(或Presenter、ViewModel)到view到util等等,一整套流程作下來,到最後纔可能運行起來看看是否是對的,有的時候哪怕全部代碼都寫完了,也不必定能驗證是否是對的,好比說後臺尚未ready等等。總之,在沒有單元測試的狀況下,咱們須要等到最後一刻才能手動驗證代碼是否是對的,而後發現原來這裏錯了一點,那裏少了一點,而後一遍一遍的把app運行起來,改一點運行一遍。。。

當我開始寫單元測試以後,我發現這個過程實在是太漫長了,我喜歡寫完一部分功能獨立的代碼,就能馬上看到他們是否是正確的。若是不是的話,我能夠馬上就改正,而不用等到全部代碼都寫完整。要達到這點,那就只有寫單元測試了。

固然,哪怕有單元測試,最後仍是要作一遍手動測試工做,然而由於前面我已經保證每個單元都是對的,最後只不過是驗證每一部分都是正確的串聯起來了而已,這點相對來講,是很容易的。因此最後所須要的手動測試,能夠少不少,順利不少,也簡單得多。

最後,是由於我懶

如前所述,若是沒有單元測試的話,那就只有手工測試,把app運行起來,若是有錯的話,改一點東西,再運行起來。。。這個過程太漫長太痛苦,對於一個很懶的人來講,若是能寫代碼來代替手工測試,每次寫完代碼只須要按一次快捷鍵,就能夠直接在IDE裏面看到結果,那是多爽的一件事!因此衝着這點,我也不想回頭。

我記得上一次使用「把app運行起來」這種開發方式,仍是由於調試一個動畫效果。由於動畫效果是很難單元測試的,那就只有改一點代碼,跑一邊app,以爲不對,再改一點,跑一邊,這樣來來回回反反覆覆,那感受真是。。。

單元測試給我帶來了什麼

前面講了爲何我要寫單元測試的緣由,接下來說講用了單元測試這種寫代碼的方式之後,給我帶來什麼樣的好處。這根前面講的「緣由」有部分重合的地方,然而也有不同的地方。

更快的結果反饋

這點前面講過了,有單元測試的幫助,我能夠寫完一個獨立的代碼單元,就馬上驗證它的正確性,這跟須要完成全部代碼再把app運行起來手動測試相比,是一個更快的反饋循環,能更快的發現代碼是否正確,也更快的獲得一種成就感。

更少的bug,或者說更快的發現bug

正如上面所說,咱們沒有作這樣的先後統計,來證實有了單元測試之後,咱們app的bug少了多少。然而,我本身的經驗是,我已經不知道多少次覺得只是作了一點小改動,不會有任何問題,結果一跑單元測試,發現仍是改出問題來了。從這點來講,單元測試幫助我發現了很多問題,至少是更快的發現了問題。不少時候,這些問題是由於不當心疏忽了而致使的。然而話說回來,大部分bug不都是由於不當心疏忽了,不少狀況考慮到,或者是考慮錯了而致使的嗎?

你或許會以爲,本身很厲害很專業,必定不會有這種「疏忽」,寫的代碼必定是沒有bug的。然而事實是,再厲害的人,都有狀態很差的時候,都有情緒不高的時候,都有感受比較累的時候,都會受到或多或少外界的干擾,這種時候都是很容易犯錯的。這個跟厲不厲害,專不專業其實沒有關係。李世石多麼專業,在跟AlphaGo比賽的時候,不是依然會失誤,會犯錯嗎?這個時候若是有那麼一層保障,來防止你不當心犯錯,豈不是更好的一件事情?

節約時間

對於安卓開發來講,一遍一遍的運行app,再執行相應的用戶操做,看界面是否顯示正確的結果,經過這種方式來測試本身的新代碼、重構是不是正確的,這是很是浪費時間的一件事情,並且效果還很差。有了單元測試,我如今開發過程當中幾乎已經不用把app運行起來了,速度相對來講快多了。

此外,由於單元測試能幫我減小bug,從而也減小了調試bug,fix bug的時間。一個切身感覺是,自從開始寫單元測試之後,我啓動AndroidStudio的debugger的次數明顯減小了。這也是單元測試節約時間的地方。

固然,這個結論也是自我感受的結果。寫單元測試須要時間,這也是不可否認的事情,至於有單元測試是否真的更快,快了多少,我沒有具體的統計數據,因此很難給出一個確切的答案。

這裏須要重點說一下的是,你爲新代碼寫的單元測試,不只僅是能在目前你此次寫新代碼的時候起了做用,它的做用更體如今之後重構代碼的時候,你能夠很快速,很安全的進行重構。這點每每你們會忽略,因此會以爲在單元測試上花費的時間「不值得」。

更好的設計

當你爲本身的代碼寫單元測試的時候,尤爲是採用TDD的方式,你會很自覺地把每一個類寫的比較小,功能單一,這是軟件設計裏面很重要的SRP原則。此外,你能把每一個功能職責分配的很清楚,而不是把一堆代碼都塞到一個類裏面(好比Activity)。你會不自覺的更偏向於採用組合,而不是繼承的方式去寫代碼。這些都是很好的一些代碼實踐。

至於爲何TDD可以改善代碼的設計,網上有不少的文章去分析和論證這個結論。我看到比較印象深入的一句話是(具體在哪看的搜不出來了):當你TDD的時候,你是從一開始,就從一個代碼的使用者,或者說維護者的角度,去寫你的代碼。這樣寫出來的代碼,天然會有更好的設計。

更強的自信心

有單元測試來保證你的代碼是對的,這對於你寫代碼、發佈代碼、重構都提供了信心保證,沒有那麼多的擔憂,從而工做起來也更快樂更開心。作人吶,最重要的是開心。。。

而開心,也能讓你變成一個更好的程序員。

沒有時間寫單元測試?

前面大概講了講我爲何要寫單元測試,以及單元測試給我帶來的好處,這些其實若是你們去google 「why unit testing」,估計會獲得相似的答案,然而依然會有不少人不寫單元測試。若是問爲何的話,那麼獲得最多的回答,估計是:沒有時間。

那麼,寫單元測試真的須要不少時間嗎?爲何多數真正寫過單元測試的人會說,寫單元測試能夠節約時間呢?在這裏,首先要認可兩點。。。

1. 單元測試,的確是一門須要學習的技術。

不只須要學習,並且你要學習的東西還真很多,你要學習JUnit的使用,你要學習Mokito的使用,Robolectric的使用,依賴注入的概念和使用等等等待。此外,在剛開始的時候,你的確也會遇到不少坑,現有代碼的坑,Android的坑,Robolectric的坑等等。這個在安卓開發這邊顯得更是如此,由於Android開發環境是公認的最不利用寫單元測試的環境之一。你須要花一些時間去學習如何處理,或者是繞過這些坑。

2. 在一個現有的,沒有單元測試的項目裏面加入單元測試,會須要一段時間的調整。

一個有單元測試的項目,跟一個沒有單元測試的項目相比,結構會有比較大的不一樣。所以剛開始,你會發現各類不順利的狀況,你須要去調整各部分的代碼,讓他們變的容易測試,這也是比較花時間的地方。

這種調整值得嗎?我認爲是值得的,由於容易測試的項目,每每意味着更靈活,更具有擴展性,這個前面已經提到過了。因此自己這件事情就是一件值得作的事情,更況且,測試自己又是一件很是有價值的事情。

然而等跨過了這兩道坎,單元測試還須要花不少時間嗎?根據我本身的經驗,我以爲其實不是這樣的,由於等你熟悉瞭如何寫單元測試之後,要對一個類、一個接口寫單元測試,是很容易的一件事情。若是你發現一個類很差測,每每是由於這個類的設計是有問題的。此外,你能夠慢慢的搭建本身的一套測試框架,簡化一些經常使用的繁瑣的寫法,讓寫單元測試變得更簡便快捷。再加上前面講述的緣由,整體來講,我以爲寫單元測試非但不會須要跟多時間,反而會節約時間。

小結

這篇文章簡單講述了,爲何要寫單元測試。其實,單元測試的必要性,看看那個知名的程序員必看書單就知道了。在前20本中,全部5本講述「如何寫出更好的代碼」的書,無一例外都強調單元測試的必要性。

  • Code Complete

  • The Pragmatic Programmer

  • Refactoring: Improving the Design of Existing Code

  • Clean Code: A Handbook of Agile Software Craftsmanship by Robert C. Martin

  • Working Effectively with Legacy Code by Michael C. Feathers

但願這篇文章,能讓你多一點學習和實踐單元測試的決心,由於這真的是很是值得擁有的一項技能,只是剛開始的時候,須要多一點點時間而已。

最後,若是你也對安卓單元測試感興趣的話,歡迎加入咱們的交流羣:

參考連接:What is the single most influential book every programmer should read?

做者 小創 更多文章 | Github | 公衆號

相關文章
相關標籤/搜索