Kotlin TDD - 以 Android 開發為例

活動訊息
Kotlin TDD - 以 Android 開發為例

不論您是 TDD 新手,想要學習如何在開發流程裡導入、或是曾經嘗試過 TDD 但不要領,想要從別人的經驗中學習,透過這場分享,您將對 Kotlin TDD 及 Android 開發有更進一步的認識。

活動日期:2020 年 11 月 10 日

活動時間:PM 08:30

主辦單位:

 
講者簡介
陳瑞忠
Android Developer

瑞忠平時喜歡研究新技術,也樂於在網路上分享技術文章,尤其對 Android 開發有高度的熱情。曾以「Android TDD 測試驅動開發」為題參與第 11 屆 iT 邦幫忙鐵人賽榮獲佳作,近期開始在線上開設 Android 系列開發課程。

聽過測試驅動開發(Test-driven development,簡稱 TDD),也知道 TDD 可以協助開發出符合需求的產品,但實際在專案導入時還是卡在對流程不熟悉、工具操作不順暢而心生放棄的念頭嗎?本次線上技術講座邀請到瑞忠(Evan)為大家示範如何在撰寫 Kotlin 或 Android 專案時以 TDD 的流程進行開發,加強大家在 IntelliJ IDEAAndroid Studio 裡的 TDD 實務技巧。

主題分享

瑞忠以一個具有加法(add)方法的 Math 類別,說明單元測試的基本觀念及如何用測試程式來驗證需求,做為單元測試的開場。在有了單元測試的基本觀念後,瑞忠從流程的角度,說明 TDD 的三個步驟:第一,先撰寫一個失敗的測試。(紅燈)第二,撰寫剛好可以通過測試的產品程式碼。(綠燈)第三,在不改變程式碼的外部行為下改進原始碼。(重構)在實務開發時,就是依循 TDD 的工作流程,依照需求完成一個又一個的單元測試。

什麼是 TDD?

瑞忠設計了「保齡球計分程式(Bowling Game Kata)」、「踩地雷遊戲」及「登入畫面」共 3 個範例,讓大家實際體驗 TDD 的開發流程。首先從保齡球計分程式開始示範 Kotlin TDD,經分析保齡球計分規則後整理出 5 個測試案例。開始撰寫程式碼時,瑞忠大量的使用 IDE 裡的功能,包括 Live Template、Postfix Completion、Show Context Actions,讓他可以先專注在寫測試程式碼再用 IDE 產生 Production Code,除了不會中斷 TDD 的開發思緒外,還可以減少打字時間。加上適時以重構工具將測試程式碼整理成更好理解、更符合人類語意的結構及命名,同時配合 Git 做版本管理,依照 TDD 小步快走的精神,在 20 分鐘左右的時間就完成 Bowling Game Kata 的範例。

Android TDD

在示範完單純的 Kotlin TDD 後,瑞忠接著點出 Android 開發時,因為多了 UI 而不知道該怎麼測試的問題。因此他以踩地雷遊戲及登入畫面兩個範例,示範在多了 UI 後的 TDD 流程,並強調在實作 Android TDD 時,應該從使用者的角度出發,把需求拆解成一步一步來實作。而導入 TDD 的目的也不只是通過測試,而是在滿足測試條件的過程中完成 UI 的所有細節。最後,瑞忠從測試金字塔的概念跟大家分享哪些測試最重要?哪些測試最需要寫?以及撰寫測試時的順序該怎麼安排。

測試金字塔

從瑞忠的示範裡可以看出,在撰寫程式碼的時候,他會先專注在測試程式,並從類別使用者的角度去「描述」程式的行為,過程中出現的紅色錯誤都先暫時不管,等到測試程式的雛型完成後,再用 IntelliJ IDEA 或 Android Studio 裡的快速修復功能,讓 IDE 自動幫我們補齊缺漏的程式架構。換言之,產品程式碼是用 IDE 「長出來」的。這樣的好處是,開發者只需要關注在最重要的測試程式碼及核心業務邏輯即可,至於程式碼的架構、撰寫風格、放置的位置都可以在 IDE 的輔助下自動產生、排版、重構,大大減少撰寫測試程式的時間,並提高使用 TDD 開發的效率。另外,瑞忠也在 IntelliJ IDEA 及 Android Studio 裡安裝 IdeaVim Plugin,讓他可以將 Vim 的編輯效率與 IDE 強大的功能彼此結合。總結來說,TDD 可以讓開發者先想清楚需求是什麼,讓思緒跟著需求,在工具快速產生程式碼同時也避免 Over Design,小步快走來完成專案實作。

問答

Q. 使用 TDD 之後,測試的數量變很多,有些是同個概念的東西(例如 API 一定會有 Success 或 Fail),測試數量太多感覺也不好維護,這部分要怎麼取捨?

會把每一個狀況都獨立成一個測試是希望當有錯誤發生時可以馬上抓到,所以會拆得比較細,也比較符合 TDD 裡小步快走的精神。若是真的想要減少測試數量,像本次範例裡 Login 這種較單純的需求,可以試試 AssertJ 裡的工具將兩個測試寫在一起,但還是要提醒要適度使用。

Q. 當團隊採用 TDD 之後,Unit Test 數量變多,如果日後程式邏輯(業務邏輯)變更,要怎麼知道哪些測試要變更?

當業務邏輯變更時,應該回頭修改測試案例,將新的邏輯加進去,同時讓 Production Code 通過測試。假如加入新的邏輯後測試還是可以通過,一種原因可能是剛好原本的 Production Code 就滿足新的邏輯、另外一種原因就是你的測試案例可能是有問題的。

Q. 該怎麼去判斷要先做 Unit Test 還是先做 UI?

在判斷時要以「需求」為依據。在這次的兩個示範裡,剛好一個是先做 Unit Test、一個是先做 UI Test。因為踩地雷範例的第一個需求是要先看到格子,而格子背後有資料來源有邏輯,所以需要先做 Unit Test 接著才做 UI。而 Login 範例的第一個需求是要先看到輸入帳號密碼的畫面,因為不需要測試邏輯,所以會先實作 UI。等到實作 Login 按鈕的動作、牽扯到 ViewModel 等邏輯時,才會開始寫測試。

Q. 剛才看到很多都會把 Function 抽出,進行 Refactor 保齡球的案例裡,All Strike、Spare 的 Score 計算等等也抽了出來,是有什麼好處?在測試的層面會有什麼分別?

雖然將這些邏輯以重構工具抽出來成 Function 在測試結果或程式碼執行來說並沒有差別,但若能以具備語意、好理解的名稱來命名這些 Function 的話,將會對維護這些程式碼的開發者有很大的幫助。所以會建議大家不論是測試程式碼或 Production Code,都可以利用這種重構技巧來提升程式碼的易讀性。

Q. 隕石式開發適合導入 TDD 嗎?

若需求時常變更,則導入 TDD 一樣可以帶來好處。透過修改測試案例,可以確保 Production Code 仍是符合需求,也不擔心 Production Code 改壞。

Q. 測試命名上有什麼建議嗎?

測試的命名主要是要讓接手維護的人易讀、好懂,所以我會建議在命名時要正確表達出需求情境。為了易讀性的原因,在範例裡用的是蛇底式的命名方式,或許不符合 Kotlin 常用的語法風格,但我覺得相對駝峰式命名會更好讀。甚至為了提升理解,有些人也會用中文來命名。這些都是為了讓開發者更快了解需求情境而可以做的調整。

問卷調查

本次講座進行過程中也進行了簡單的調查,在這邊一併分享結果給大家參考:

Q. 您用 Kotlin 開發什麼樣類型的專案?

您用 Kotlin 開發什麼樣類型的專案?

Q. 目前開發時是否有採用 TDD 開發?

目前開發時是否有採用 TDD 開發?

從問卷的結果看來,不少朋友都正在開發 Android 專案,不過實際採用 TDD 開發的比例還不高,許多朋友都是聽過或還不得要領。希望透過瑞忠的示範,可以讓大家體認到只要正確的使用 TDD,加上開發工具的輔助,在開發效率及品質方面都會有顯著的提升。若您想深入了解 Kotlin 及 TDD 的主題,我們之後仍會舉辦更多技術講座,歡迎大家繼續參加!

講者投影片