Android修煉系列(40),Espress UI 自動化測試
theme: github
Espresso 測試框架(Android 4.0.1,API 級別 14 或更高版本)提供了 Android UI 測試的 API,用來模擬使用者與應用的 檢視互動。
本文從實踐的角度,講下 Espresso 的使用,希望也能為大家避坑。
關於更過 Espresso 的介紹,可點選 官網傳送門 。
本 demo test 自動執行效果:
Espresso
在整合 Espresso 的時候,可能會遇到一些坑,主要是些依賴的版本衝突問題。具體版本沒有要求,如果編譯不通過,可參考下我的 demo:
在 gradle
檔案中我引用了這幾個 androidTest
庫:
java
androidTestImplementation "androidx.test.ext:junit:1.1.4-alpha06"
androidTestImplementation "androidx.test:runner:1.5.0-alpha03"
androidTestImplementation "androidx.test:core:1.4.1-alpha06"
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.0-alpha05'
如果使用 kotlin,還需要引入:
java
androidTestImplementation "androidx.test.ext:junit-ktx:1.1.4-alpha06"
androidTestImplementation "androidx.test:core-ktx:1.4.1-alpha06"
androidTestImplementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.6.10"
注意 espressoVersion
的當前最新版 3.5.0-alpha06
測著有問題,ViewMatchers
時會報錯,可以使用 3.5.0-alpha05
。
java.lang.IncompatibleClassChangeError: androidtest class 'org.hamcrest.stringdescription' does not implement interface 'java.lang.iterable' in call to 'java.util.iterator java.lang.iterable.iterator()'
API 元件
Espresso 的主要元件包括:
- Espresso - 用於與檢視互動(通過
onView()
和onData()
)的入口點。此外,還公開不一定與任何檢視相關聯的 API,如pressBack()
。 - ViewMatchers - 實現
Matcher<? super View>
介面的物件的集合。我們可以將其中一個或多個物件傳遞給onView()
方法,用來查詢特定檢視。 - ViewActions - 可以傳遞給
ViewInteraction.perform()
方法的ViewAction
物件的集合,例如click()
。 - ViewAssertions - 可以通過
ViewInteraction.check()
方法傳遞的ViewAssertion
物件的集合。在大多數情況下,我們可以使用 matches 斷言當前選中檢視的狀態。
下面來寫個示例:
api 都很小巧,體會下,應該就可以上手了。
三板斧,查詢檢視 -> 對檢視操作 -> 對檢視斷言。
簡單測試
EspressoUiActivity 類中包含一個 Button
和一個 TextView
。點選該按鈕後,TextView
的內容會變為 "espresso"
。
- 在
src/androidTest/java/
路徑下編寫測試類EspressoUiTest
,注意activityScenarioRule
不能忘:
- 執行用例結果:
AdapterView測試
AdapterView
可從介面卡動態載入資料。最常見的 AdapterView
是 ListView
。和 LinearLayout
不同的是,其有時只能將一部分 AdapterView
子檢視載入到當前檢視層次結構中。使用簡單的 onView()
將找不到當前未載入的檢視。
Espresso 為此提供一個單獨的 api onData()
,該 api 能夠先載入相關介面卡資料,並在對其或其任何子級執行操作之前使其處於聚焦狀態。
來個示例:
EspressoUiActivity 類中包含一個 ListView
和一個 TextView
。點選該item後,TextView
的內容會變為 "item$position"
。
- 在測試類
EspressoUiTest
中使用onData
,模擬使用者操作:
- 執行結果:
RecyclerView 測試
關於 RecyclerView 的 UI 測試,我們需要引用依賴庫:
java
androidTestImplementation 'androidx.test.espresso:espresso-contrib:3.5.0-alpha05'
來個示例吧,跟 AdapterView 使用類似:
EspressoUiActivity 類中包含一個 RecyclerView
,程式碼見下。
這是介面卡類 CustomAdapter
部分程式碼,就是取 middle' item 賦值 “middle”,其他 item 賦值 “item$position”:
- 在測試類
EspressoUiTest
使用RecyclerViewActions
提供的介面,來模擬使用者操作:
- 沒啥特殊的,執行:
RecyclerViewActions 還提供了 api:
scrollTo()
- 滾動到匹配的檢視。scrollToHolder()
- 滾動到匹配的資料檢視持有者。scrollToPosition()
- 滾動到特定位置。actionOnHolderItem()
- 對匹配的檢視持有者執行檢視操作。actionOnItem()
- 對匹配的檢視執行檢視操作。-
actionOnItemAtPosition()
- 在特定位置對檢視執行檢視操作。 -
再來看個
scrollToHolder()
的示例,可以自定義個匹配器,在matchesSafely
編寫匹配條件(發散下,我們完全可以通過 Holder 做更多的匹配工作,甚至與外部傳入的其他 Matcher 共同作用):
自定義Matcher
EspressoUiActivity 類中包含一個 EditTest
,其 hint
屬性為 “please input”。
現在編寫一個 withHint
方法,將 BoundedMatcher
用作基匹配器,用來匹配 EditTest
的 hint
。
現在就可以使用自定義的 withHint
介面了,即可以接收 String
型別,也可以接收 Matcher<String>
型別:
當然使用 TypeSafeMatcher
實現也沒問題,大家靈活運用吧:
isDisplayed 和 doesNotExist()
isDisplayed 表示 view 被展示出來了,與 not(isDisplayed) 對應,使用起來很簡單。
EspressoUiActivity 類中包含一個 Button
,處於 Visible 狀態,點選後置於 Gone / Invisible 狀態。
- 在測試類
EspressoUiTest
中,使用isDisplayed
檢查:
- 執行 ok
doesNotExist 表示檢視不見了,即相應檢視從檢視層次結構中消失了,不是隱藏。下面來個例子:
EspressoUiActivity 類中包含一個 Button
,處於 Visible 狀態,點選後從父佈局中移除。
- 在測試類
EspressoUiTest
中,使用doesNotExist
檢查:
- 執行ok
Espresso-Intents
Espresso-Intents 是 Espresso 的擴充套件程式,它能夠幫助我們 從下面2個方面來驗證 Intent
:
- 驗證傳出的
Intent
攜帶的資料 - 模擬
ActivityResult
使用 Espresso-Intents 需要引入依賴庫:
java
androidTestImplementation 'androidx.test.espresso:espresso-intents:3.5.0-alpha05'
下面分別介紹下,怎麼編寫測試用例:
send Intent
EspressoUiActivity 類中包含一個 Button
。點選該按鈕後,會跳轉到另一個 Activity
介面。
- 在測試類
EspressoUiTest
中,使用Intents.intended
介面:
- 是不是很簡單,執行:
ActivityResult
EspressoUiActivity 類中包含一個 Button
和 一個 TextView
。點選 Button
後,會跳轉到另一個介面 EspressoUiActivity2
,返回時, TextView
的內容變為 ActivityResult
攜帶的結果。
- 在測試類
EspressoUiTest
中,使用Intents.intending
介面,同樣別忘了Intents.init()
:
- 類似於 mork,執行看下:
一定要注意,使用了 ActivityResultLauncher<Intent>
,就不能直接 startActivity
了,可以使用 startForResult.launch
。
多視窗
當一個介面存在多個視窗時,Espresso 可能會迷惑,不知道哪個是要互動的目標視窗,這個時候,就需要我們手動指定,例如:AutoCompleteTextView 這類自動補全的 帶下拉選單的 View。
來個示例吧:
EspressoUiActivity 類中包含一個 AutoCompleteTextView
,當輸入 “Red” 時,下拉選單會補全為 “Rea Sea” 可供點選。
- 在測試類
EspressoUiTest
中,我們需要inRoot(withDecorView(..))
指定視窗:
- 執行ok
注意,並非所以視窗 Espresso 都識別不了。像 AlterDialog 這類,使用起來跟普通 view 一樣簡單。
來個示例吧:
EspressoUiActivity 類中包含一個 Button
和 一個 TextView
,當點選 Buttton
後。會彈出 AlterDialog
展示 item 列表供點選,點選會在 TextView
顯示。
- 在測試類
EspressoUiTest
中,直接使用onData
api 即可:
- 執行ok
好了,本節就到這裡了,程式碼我都上傳 gitHub 了。
本節完。
參考資料
- http://developer.android.com/training/testing/espresso?hl=zh-cn
最後附上備忘錄: