工程師在應徵職位前,常常說要去刷 Leetcode;或者是有些公司會把自家的考題放上 Codility,讓申請者去寫考題,高效率篩選出適任的工程師。不管是 Leetcode 或是 Codility,其背後的驗證機制,其實就是在跑測試。
Codility 比較有趣的是,它還會紀錄受試者的寫 code 流程,把應徵者的思考流程及程式碼易讀性交給考官斟酌。
寫測試的目的就是要確保程式碼品質,因為(1)自己開發新功能可能會弄壞以前寫的 code 或 (2)團隊協作動到彼此的 code,跑個 Uinit test 馬上就知道!這可以降低日後的維護成本;對於會 TDD (Test-Driven Development)的工程師來說,甚至是加快開發速度。
程式碼品質
程式碼的品質最主要的就是:準確(Correctness)、高效(Performance)、易讀性(Readability)跟架構(Structure)。測試主要處理的就是準確跟高效。
可以看到上圖, Codility 主要就是會做 Correctness Test 及 Performance Test,來確保你的程式碼有達到正確,且效率高。Correctness Test 很好理解,就只是確保你的程式是正常運行,出來的結果跟預想的一樣; Performance Test 就是看運行效率,最常見的是在輸入的資料量很大時(例如,臉書有上億用戶,要怎了快速地找到朋友的朋友),程式能不能在時限內完成。
測試的核心 — 結果符合預期
還記得高中物理嗎?就是給一個初始值,經過一個變化量,變成預期的結果。測試真的很簡單~
Test-Driven Development 加速開發
當自己還是不會寫程式的菜鳥時,測一個東西,就只能寫完程式碼之後,等 Xcode(IDE) compile 完,再等模擬器開啟,才知道結果(當然用 swift playground 是另一回事,但是 Objective-C 沒有 playground),而且日後刪掉測試專案就沒辦法重複驗證。
想想看,若是你寫的功能在 App 使用流程比較尾端的部分,是不是就要先做一堆點點按按才能看自己要的結果?直接用 XCTest 寫,不用重複開模擬器,馬上就知道結果啦~
另外,寫測試也可以在主程式下斷點或是把東西 print 出來,這樣 debug 會方便很多!
範例實作
請在 String 上加寫一個 extension function,輸出字元順序相反的 reversed string。
測試先行。我一開始給定一個 string 的內容是 "David Wu"
,經過一些變化之後,我期望收到的結果是"uW divaD"
。
寫正式程式碼
按 ⌘+U 跑測試,發現錯誤(red❌)
觀察到是最中間項的問題,修正程式碼(把 0...n/2
修正為 0..<n/2
)
再按 ⌘+U 跑測試,成功!(green✅)
這段程式碼,其實有更好的做法來避免計算 n/2 的問題,叫做 Two-pointer Technique。我們來重構(refactor)這段程式碼。
再按 ⌘+U 跑測試,成功!(green✅)
剛剛這整個過程經歷了 red❌ → green✅ → refactor,那便是所謂的 Red Green Refactor pattern。
為什麼要有這個 pattern 呢?若是寫的測試無論怎麼寫都是錯的,那表示被測試的程式碼有誤或尚未完成;若是被測試的程式碼被隨意更動,測試都會過的話,那表示這段測試碼是寫假的。因此,才會要經過一個完整的 Red Green Refactor pattern 才能確保測試碼與被測試碼是有效的。
總結
寫 Unit Test 可以讓自己對自己的程式碼更有信心,伴隨 TDD 可以加速開發速度,何樂而不為?
稍加進階的就是非同步的測試(例如,網路服務)以及 mocking,請見下一篇 Unit Test 教學:非同步測試 API。