今天我們要正式進入UI測試的Integration Test部份,為什麼在測試UI的時候我們直接進入Integration部份而不是從Instrumentation Unit Test來講,因為測試UI的時候通常都會一次包含許多UI元件,很難也比較沒有意義去做單一元件行為的測試。例如你Press Button,然後跟Server要求一個Callback回來顯示在TextView上面,這個很簡單的測試就包含了Button的ClickListener跟TextView的setText兩個元件各自的邏輯,所以一般都以Integration的方式來測試UI的互動正確性。
那我們要怎麼寫測試程式來做UI的自動化操作呢?這時候就需要Testing framework的幫助,在Android的UI Testing framework有蠻多個,主要有三個
Roboletric的特點是不用模擬器就可以測試UI部份,什麼,之前不是才說UI一定要有Launch application process才能做。因為Roboletric把整個Android Framework mock起來用一般JVM的方式來執行,不是跑在Android zygote上,所以它可以用本地單元測試的方式來測試UI元件,可以把TDD那套也用來Android UI元件的測試 所以說你也可以把它視作是有UI元件的Local Unit Test。優點是速度很快因為不用跑在機器上,缺點就是它是mock framework,因為它不用跑在機器上因此不能做真實環境的測試,如End to End Test。UiAutomator的強項是在跨App的測試,而Espresso則是對於單一Application UI的測試及驗證功能強大,不僅可做單機的Integration Test更支援非同步呼叫的End to End Test。也讓Google大力支援希望大家以Espresso做為Instrumentaion Test的主要選項,從Android Studio內建Espresso Recorder的功能就可以看出來。
這些testing framework各有各的優點,沒有說誰比較好,端看你測試的需求是什麼。我們這邊會從Espresso來著手介紹,必竟它很容易上手可以做Integration及End to End Test也在Android Studio內建支援。再加上Android近年Emulator全面改用x86系列的軟體模擬後效能變好,執行起來已經順暢許多了。
我們先不要安裝Espresso的Library一開始從Android Instrumentation Test的環境配置來看一下,Android是怎麼做測試的。
可以看到我們新專案的配置在src下面有一個叫androidTest的資料夾,在androidTest下面又有java/com.daniel.demotest的路徑。也就是Android幫我們預設路徑如下
src/androidTest/java/<"packageName">
我們只要放在這個資料夾下的檔案都會預設執行Instrumentation來測試,因此必須要Launch android process起來,即便你只是做基本的Unit test都可以。
我們看看預設的測試檔案如下ExampleInstrumentedTest.kt
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.daniel.demotest", appContext.packageName)
}
}
當你點選檔案執行時,你會發現它要求我要跑模擬器才行(用實體手機接USB也是可以跑),可是明明code裡沒有任何UI測試,也就是呼應前面說的只要放在這個資料夾的檔案都會被當成要用Intrumentation的方式來測試,即便預設的測試code只是在Day 16提到的Intrumentaion Unit Test,只是利用context做一些事。所以Android去驅動Intrumentation Test的用法很簡單,你只要把被測試檔案放androidTest資料夾下就會告知AndroidJUnitRunner開設備來執行。
我們開始來進入Espresso的部份吧,首先要先加入build.gradle的dependency部份,第一個是test rule的部份,用來跟AndroidJUnitRunner宣告測試的預設條件,例如要呼叫的Activity,Intent等。第二個當然是Espresso的核心元件囉。
dependencies {
androidTestImplementation 'androidx.test:rules:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}
記得我們之前說過,Unit test無法測試UI的部份,即便你用MVP或MVVM的模式已經確認Data正確無誤,但沒看到UI正確顯示出來總是心不安。那我們就來試試看吧。我們來測試之前在MVP範例提到的MainActivity裡的TextView是否能正確顯示出字串。
我們利用Android Studio預設的檔案來修改,測試function命名為testMainActivity()然後把MainActivity開啟並檢查textView是否有顯示出來。有沒有很簡單,利用Espresso你不用考慮什麼設計模式,直接測試UI簡單又快速。
class ExampleInstrumentedTest {
//宣告要測試的Activity,請Runner執行Test區塊前直接開啟
@get:Rule
val activityTestRule = ActivityTestRule(MainActivity::class.java)
@Test
fun testMainActivity() {
//請espresso去找id為textView並且判斷是否有顯示出來
onView(withId(R.id.textView)).check(matches(isDisplayed()))
}
}
有人可能會覺得同樣測試一個MainActivity,用單元測試的方式要改一堆東西,用Espresso只要幾行就可以了。話雖如此,兩者目的一樣但用途不同,之後還會再詳做討論。下一章節會開始Espresso的詳細介紹。