我們來為第二單元「在Android 的測試」小結一下,在這個單元我們介紹了Android的各種測試:
Local Test
Local Test的執行速度最快,我們應該讓App的測試儘可能都是Local Test。儘可能讓你的商業邏輯與View所負責的部分拆開,你就可以有更多的Local Test。
Instrumented Test
Instrumented tests ,當被測試的物件與Android framework有關連時,就需要使用Instrumented tests。這個測試需要在模擬器或實體機器執行,執行的速度較慢。
UI Test
UI測試主要用來測試使用者與App的互動,當然也是需要在模擬器或實體機器上執行。
Local Unit Test 使用 Robolectric
Robolectric讓你可以用單元測試的方式來測試與Instrumented tests。
這兩種測試都是寫在androidTest的目錄下,因為都是需要模擬器或實機。Instrumented 的中文的意思是「設備、儀器」指的就是在機器上的測試,跟UI Test的差異在於UI Test特別強制使用者的互動。例如你要測試儲存資料SharedPreferened就是Instrumented test。或是要測試Service的話,也是Instrumented test。
我們在這個單元分別使用了UI測試:Espresso與Robolectric來測試View的行為,而這兩種有什麼差異呢?
UI測試的測試目錄在androidTest。
Robolectric的測試目錄在test。
UI測試在模擬器或實機測試。
Robolectric的測試在JVM測試。
UI測試失敗時,較慢找到失敗的原因。
Robolectric屬於單元測試,較快找到失敗的原因。
除了這兩個差異,更重要的是測試的行為是不一樣的。
在UI測試Espresso的測試註冊用驗證是否有「註冊成功」的字來確認。
@Test
fun rightPassword_should_startActivity() {
//輸入正確的帳密
inputRightRegisterData()
//點選註冊按鈕
onView(withId(R.id.send)).perform(click())
//註冊成功,導至成功頁。
onView(withText("註冊成功")).check(matches(isDisplayed()))
}
而在Robolectric的測試註冊成功則是驗證Intent。
@Test
fun registerSuccessShouldDirectToResult() {
//arrange
val shadowActivity = Shadows.shadowOf(activity)
val userId = "A123456789"
val userPassword = "a123456789"
activity.loginId.setText(userId)
activity.password.setText(userPassword)
//點下註冊按鈕
activity.send.performClick()
//驗證註冊成功時,是否有開啟ResultActivity
val nextIntent = shadowActivity.nextStartedActivity
assertEquals(nextIntent.component!!.className, ResultActivity::class.java.name)
assertEquals(1, nextIntent.extras!!.size())
assertEquals(userId, nextIntent.extras!!.getString("ID"))
}
Robolectric 的重點在於,當帳號、密碼欄位設定為錯誤的帳密時,傳給Intent的值是否正確。UI測試的重點在於,當帳號、密碼欄位輸入錯誤的帳密時,是否有在畫面上看到註冊成功的字。UI測試是有「輸入文字」的行為。在Robolectric,我們是直接讓EditText的Text給值,跟UI測試的開啟鍵盤輸入文字是不一樣的。所以UI測試較能測試到使用者真實的互動。在選擇用哪一種測試時,你需要同時考慮的是測試的效率與使用者的互動。
從測試金字塔再來回顧一下。
圖片來源 Google IO 2017
如果以測試金字塔來說,UI測試就會算是第二層的Integration test,而Robolectric則會是第一層的單元測試。
這裡有一個我們沒有提到太多的E2E測試,端點對端點的測試。當App會連接WebAPI時,這時App與WebAPI就是兩個不同的端點。以註冊帳號為範例,這樣的測試就必須同時驗證註冊後是否真的有資料進到資料庫。這樣的測試是最耗時的,也是最容易失敗的,如果當下的網路異常,或資料庫裡已有一筆相同被註冊過的帳號,皆會導至註冊失敗。在下個單元介紹WebAPI時,將再介紹在有呼叫WebAPI功能的App做測試。
Android 的每一種測試是對映到測試金字塔的哪一層其實並不是很重要,測試金字塔要傳達的概念只是測試應有不同的層度:
1.越上層成本越高,測試越慢。
2.越上層的測試應越少。
如果反過來的話,則會是像下圖的這個「測試冰淇淋」,最上層的手動測試最多,最下層的單元測試最少。可以想像這樣的測試方式有多耗時。
第二單元「在Android的測試」就到這告一個段落,下個單元我們將開始進入到Android測試的架構篇,介紹MVP及MVVM的架構,並為範例加入串接WebAPI,同時開始處理非同步的議題。
出版書:
Android TDD 測試驅動開發:從 UnitTest、TDD 到 DevOps 實踐