配合分支:day17/start,Repo: 按我
來寫第一個 Android Unit Test 吧!Unit Test 聽起來很高大上啊!今天就來寫!不過,照片日記有用 Android Framework 的東西,例如:Activity、ViewModel⋯ 等,不能寫純粹的 Unit Test。還記得前面說 Android 和 Java 的關係難分難捨嗎?要測試含有 Android Framework 提供的 class,就需要跑 Android Unit Test。
特點是需要跑在虛擬機器裡頭,真的 run 起一個 Android 環境來跑這些測試。
聽起來很複雜⋯ 但其實只要將測試檔案放在 app/src/androidTest 的目錄之下,接著照 app/src/main 後面的 App 路徑去建立檔案,Android Studio 就會自動辨認 androidTest 下的檔案是 Android Unit Test 了。
這次就來測試「寫入日記」的檢查,寫成 Test。照慣例,先來偷偷的做一點小調整。
先來強化 title 和 note 的判斷,也把使用者多打的空白移除。
可以用 Kotlin 提供的 trim() 方法(文件),把一個字串「前面」和「後面」的空白字元移除。
先來調整這一塊 👇🏼

改成這樣,要寫入被 trim() 後的文字才行。

寫好功能之後,準備來寫相關的測試!
在寫測試以前,要先知道 Unit Test 有一些約定俗成的格式。知道之後再介紹 Unit Test 與測試作業流程有關的方法。
每一個測試最簡單可以分成三個階段
我在註解裡頭會詳細的將三個階段在做什麼記錄下來,一方便「真的理解在測什麼」,另一方面為了 3 個月後的自己做點好事⋯。
「照片日記」主要用這種風格的註解:
// Given:建立日記狀態物件,並記錄目前已有幾篇日記
// When:從 public saveDiary 傳入兩個都只有空白的欄位
// Then:確認沒有新增日記,並顯示既有的驗證錯誤
這個 Given - When - Then 就是上述提到的三個階段。另一種常用的分三階段的註解是:
// Arrange:建立日記狀態物件,並記錄目前已有幾篇日記
// Act:從 public saveDiary 傳入兩個都只有空白的欄位
// Assert:確認沒有新增日記,並顯示既有的驗證錯誤
基本上大架構是一樣的。
Android Unit Test (或 AndroidTest) 需要有這行 @RunWith(AndroidJUnit4:class)。

測試寫在 class 的 {} 裡頭。有小老鼠(其實這東西正式名稱是 Annotation)的方法比較特別。像是 @Before 和 @After。
@Before
fun whateverNameForIt() { // 👈🏼 名稱可以自行取名
// 每次「任何一個」測試「開始以前」,都會執行這裡
}
而 After 則是反過來
@After
fun whateverNameForIt() { // 👈🏼 名稱可以自行取名
// 每次「任何一個」測試「執行結束」,都會執行這裡
}
讀者可能會想問,這兩個有啥用處?Before 通常用於準備變數、狀態,把他們變成「最初」的樣子。而 After 則用來清掃狀態。
以「照片日記來說」

這幾個方法就是每一則測試,都會去跑的 Before/After 方法。以及在 AndroidTest 取得 Android Context 的方式,和取得日記檔案的方法。(畢竟要測試 ViewModel 是否「真的有好好的去建立日記檔案」)

這個「新增日記」頁面,主要有三個想測試的:
Title 是空白,可以新增但就是沒有標題Note 是空白,可以新增,但就是沒有內文
i. 先去取一開始有多少日記
ii. 接著寫入兩個空白的,反正會被 trim 塞個 \n 換行好像蠻調皮的,但應該要可以
iii. 檢查日記大小,應該要不變。然後錯誤訊息要被顯示
Title 是空白,可以新增但就是沒有標題
i. 先去取一開始有多少日記
ii. 也是很心機,Title 塞一個 tab \t,內文寫今天下雨故意給他一堆空白
iii. 檢查日記,應該要加一個。然後去檢查最新的日記,應該要有「今天下雨」內文的。
Note 是空白,可以新增,但就是沒有內文
i. 照樣,取一開始有多少日記
ii. 再次心機,Title 塞一個含有一堆空白的「旅行」,內文留白
iii. 檢查日記,應該要加一個。然後去檢查最新的日記,應該要有「旅行」標題的。


先在 Android Studio 裡頭,將測試檔案開起來 PhotoDiaryViewModelTest。看到 class 前面的箭頭了嗎?按下去後按:Run PhotoDiaryViewModelTest 選項。
接下來就會開始跑囉!洗咖西,沒想到,居然有錯!
看看結果視窗會有說明,哪邊失敗。可以看到訊息是:

expected:<4> but was:<3>
應該要是 4 則日記,卻只有偵測到 3 則。看來是沒有成功的寫入資料⋯ 🤔
體制吧!不是我是說程式碼!其實,在加入去除空白功能的時候,偷偷留下了一些小錯誤來製造本章的節目效果,在判斷「請至少填寫標題或內容」的時候,加入了錯誤的 if 判斷條件!(登登,What!?)
請看:
if (normalizedTitle.isBlank() || normalizedNote.isBlank()) {
validationError = "請至少填寫標題或內容"
} else {
// 成功寫入,以下省略...
}
現在這樣會發生什麼事呢?原來 Title 或是 Note 只要有任一是空白的,就會被判斷為錯誤,自然不會被加入到最終結果之中囉⋯⋯
調整方式也很容易,這邊就請讀者自行調整囉。調整結束後再跑 Test,這次應該會全部通過了。
app/src/androidTest 目錄下就會被自動辨認;因為照片日記用到 Activity、ViewModel 這些 Android 的東西,所以必須用它才能測。@ 開頭的標記,用來告訴測試框架這個方法很特別;今天靠它指定哪些方法要在測試前後自動執行。{} 裡頭的 Function,每個方法就是一則測試;今天三則測試分別檢查「兩個都空白要報錯」、「Title 空白也能存」、「Note 空白也能存」。