當我們把unit test一些基本概念弄清楚後,我們進入下一個章節TDD,TDD全名是Test Driven Development,也就是測試導向開發,在還沒寫production code之前先寫測試,聽起來很難想像因為跟我們的開發習慣大異其趣。都還沒有開始寫code是要怎麼先測試呢?其實並會不太難,只要改一下寫code的習慣先看spec規畫interface再來寫production code就可以了。
開發需求:在Main page中的TextView中顯示username欄位,文字屬性16dp,置中,顏色黑色。
假設有一個開發需求如上。這時候尋常的做法就是打開designer的UI圖第一步先把layout檔建立好,然後下一步就在Activity或Fragment裡面寫一個跟server reqeust的function去更新TextView。這個做法很平常,但是如果我們實踐TDD的測試導向開發方法,我們把重點擺在正確的邏輯先寫出來再去寫UI部份,因為UI可能不斷的改變,邏輯可能一確定下來就不會變了(可能啦)。
我建議可以這樣做。第一步,先不要寫layout部份,layout只是先讓自己有一個畫面感覺,先試著把跟UI有關及無關的東西分開來,像是要對TextView操作setText這就是UI有關的部份,而跟server request data的部份就跟UI無關,因為之前說過Unit test無法測試跟UI元件instance有關的部份。
不用管細節實作,先開發介面吧
所以我們單元測試的重點就擺在UI無關的server requst部份。如果採用MVP架構的話,我們要測試的目標就是presenter的function,下一步就是建立一個presenter class把跟server有關的邏輯寫在裡面,在忍不住想要開始寫production code之前先等等,我們再回去看之前的範例,你有沒有發現我們在做單元測試的時候其實都會把presenter裡面用到的物件mock起來,被mock起來的物件其中要怎麼return value或行為都是由該測試單元來定義而不是真的去執行Activity的細節來獲得資訊,也就是在寫測試code的時候不用管presenter細節的實作,我們只要知道function名稱跟回傳型態就可以寫測試的code了,這樣講是不是比較有感覺了。
此時我們把TDD的流程畫成上圖,可分為三個階段
需求轉換
先想到UI會跟presenter要求資料,所以我們建立一個presenter的empty function叫requestUserName()用來給Activity呼叫。然後當reqeust資料完成後會需要更新UI,所以我們也建立一個function叫receivedUserName()通知Activity。
單元測試
這個時候就可以開始寫測試程式了,如同我們的範例一樣,當presenter執行requestUserName()的時候UI interface的receivedUserName要被呼叫到。直到這邊我們的還不需要有任何production code的加入就已經驗證好這個需求的邏輯了。
細節開發
單元測試完成後我們再來寫production code,你就會發現開發這個task的必要步驟已經寫好了,我的production code只要依照這些步驟來寫就好了,寫完再去執行測試就可以對照我們的production code是否有遵循unit test裡的規則來寫。
取得user清單並且在Main page上filter出first name是Daniel的user
同理我們也可以用之前在MVVM章節提過的範例來討論,PM開了一個task要把server取得的user list找出firstName是Daniel的清單顯示在Activity裡,我們一樣在ViewModel的class裡先依照這個要求建立一個private的LiveData叫users,然後用一個public getter把他包起來供外部呼叫使用。然後針對server reqeust的部份我們用一個Sever class把server reqeust介面化,當呼叫requestUsers()我們就要回傳user list,最後在ViewModel用之前提過的DIP觀念用setter的方式把Server實體放入ViewModel。針對task要求這樣unit test的interface都建立好了。然後我們就可以先寫unit test如範例所述。到這個時間點我們都還沒開始寫ViewModel裡users變數裡實作的production code喔。
最後跟上述MVP TDD流程提到的一樣,才開始去實作ViewModel裡的細節。有發現了嗎,此時不管你production code怎麼寫都須要通過這個unit test的測試,mock的測試資料已經放了3個user data但是只能顯示一個。
所以說TDD沒想像的複雜,但的確需要一點耐心來完成。一開始會花你很多時間,但如果你的code在開發時需要refactor的話,利用TDD會幫你省下許多debug時間。我們可以看下圖,我們不論是第一次開發或是refacoring重構都可以一直follow這個流程開發完立刻單元測試減少出錯機會。
有人可能會覺得這樣的好處是什麼?就只是聽起來比較厲害而已嗎?先寫test case和開發完再來寫test case這差在哪?我本身經驗規納出以下兩點
下一個章節我們來介紹TDD的加強版BDD(Behavior Driven Development)。