Android修煉系列(40),Espress UI 自動化測試

語言: CN / TW / HK

theme: github

Espresso 測試框架(Android 4.0.1,API 級別 14 或更高版本)提供了 Android UI 測試的 API,用來模擬使用者與應用的 檢視互動。

本文從實踐的角度,講下 Espresso 的使用,希望也能為大家避坑。

關於更過 Espresso 的介紹,可點選 官網傳送門

本 demo test 自動執行效果:

blog_zuo.gif

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 斷言當前選中檢視的狀態。

下面來寫個示例:

image.png

api 都很小巧,體會下,應該就可以上手了。

三板斧,查詢檢視 -> 對檢視操作 -> 對檢視斷言。

簡單測試

EspressoUiActivity 類中包含一個 Button 和一個 TextView。點選該按鈕後,TextView 的內容會變為 "espresso"

image.png

  1. src/androidTest/java/ 路徑下編寫測試類 EspressoUiTest,注意 activityScenarioRule 不能忘:

image.png

  1. 執行用例結果:

image.png

AdapterView測試

AdapterView 可從介面卡動態載入資料。最常見的 AdapterView 是 ListView。和 LinearLayout 不同的是,其有時只能將一部分 AdapterView 子檢視載入到當前檢視層次結構中。使用簡單的 onView() 將找不到當前未載入的檢視。

Espresso 為此提供一個單獨的 api onData(),該 api 能夠先載入相關介面卡資料,並在對其或其任何子級執行操作之前使其處於聚焦狀態。

來個示例:

EspressoUiActivity 類中包含一個 ListView 和一個 TextView。點選該item後,TextView 的內容會變為 "item$position"

image.png

  1. 在測試類 EspressoUiTest 中使用 onData,模擬使用者操作:

image.png

  1. 執行結果:

image.png

RecyclerView 測試

關於 RecyclerView 的 UI 測試,我們需要引用依賴庫:

java androidTestImplementation 'androidx.test.espresso:espresso-contrib:3.5.0-alpha05'

來個示例吧,跟 AdapterView 使用類似:

EspressoUiActivity 類中包含一個 RecyclerView,程式碼見下。

image.png

這是介面卡類 CustomAdapter 部分程式碼,就是取 middle' item 賦值 “middle”,其他 item 賦值 “item$position”:

image.png

  1. 在測試類 EspressoUiTest 使用 RecyclerViewActions 提供的介面,來模擬使用者操作:

image.png

  1. 沒啥特殊的,執行:

image.png

RecyclerViewActions 還提供了 api:

  • scrollTo() - 滾動到匹配的檢視。
  • scrollToHolder() - 滾動到匹配的資料檢視持有者。
  • scrollToPosition() - 滾動到特定位置。
  • actionOnHolderItem() - 對匹配的檢視持有者執行檢視操作。
  • actionOnItem() - 對匹配的檢視執行檢視操作。
  • actionOnItemAtPosition() - 在特定位置對檢視執行檢視操作。

  • 再來看個 scrollToHolder() 的示例,可以自定義個匹配器,在 matchesSafely 編寫匹配條件(發散下,我們完全可以通過 Holder 做更多的匹配工作,甚至與外部傳入的其他 Matcher 共同作用):

image.png

自定義Matcher

EspressoUiActivity 類中包含一個 EditTest,其 hint 屬性為 “please input”。

現在編寫一個 withHint 方法,將 BoundedMatcher 用作基匹配器,用來匹配 EditTesthint

image.png

現在就可以使用自定義的 withHint 介面了,即可以接收 String 型別,也可以接收 Matcher<String> 型別:

image.png

當然使用 TypeSafeMatcher 實現也沒問題,大家靈活運用吧:

image.png

isDisplayed 和 doesNotExist()

isDisplayed 表示 view 被展示出來了,與 not(isDisplayed) 對應,使用起來很簡單。

EspressoUiActivity 類中包含一個 Button,處於 Visible 狀態,點選後置於 Gone / Invisible 狀態。

image.png

  1. 在測試類 EspressoUiTest 中,使用 isDisplayed 檢查:

image.png

  1. 執行 ok

image.png

doesNotExist 表示檢視不見了,即相應檢視從檢視層次結構中消失了,不是隱藏。下面來個例子:

EspressoUiActivity 類中包含一個 Button,處於 Visible 狀態,點選後從父佈局中移除。

image.png

  1. 在測試類 EspressoUiTest 中,使用 doesNotExist 檢查:

image.png

  1. 執行ok

image.png

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 介面。

image.png

  1. 在測試類 EspressoUiTest 中,使用 Intents.intended 介面:

image.png

  1. 是不是很簡單,執行:

image.png

ActivityResult

EspressoUiActivity 類中包含一個 Button 和 一個 TextView。點選 Button 後,會跳轉到另一個介面 EspressoUiActivity2,返回時, TextView 的內容變為 ActivityResult 攜帶的結果。

image.png

  1. 在測試類 EspressoUiTest 中,使用 Intents.intending 介面,同樣別忘了 Intents.init()

image.png

  1. 類似於 mork,執行看下:

image.png

一定要注意,使用了 ActivityResultLauncher<Intent>,就不能直接 startActivity 了,可以使用 startForResult.launch

多視窗

當一個介面存在多個視窗時,Espresso 可能會迷惑,不知道哪個是要互動的目標視窗,這個時候,就需要我們手動指定,例如:AutoCompleteTextView 這類自動補全的 帶下拉選單的 View。

來個示例吧:

EspressoUiActivity 類中包含一個 AutoCompleteTextView,當輸入 “Red” 時,下拉選單會補全為 “Rea Sea” 可供點選。

image.png

  1. 在測試類 EspressoUiTest 中,我們需要 inRoot(withDecorView(..)) 指定視窗:

image.png

  1. 執行ok

image.png

注意,並非所以視窗 Espresso 都識別不了。像 AlterDialog 這類,使用起來跟普通 view 一樣簡單。

來個示例吧:

EspressoUiActivity 類中包含一個 Button 和 一個 TextView,當點選 Buttton 後。會彈出 AlterDialog 展示 item 列表供點選,點選會在 TextView 顯示。

image.png

  1. 在測試類 EspressoUiTest 中,直接使用 onData api 即可:

image.png

  1. 執行ok

image.png

好了,本節就到這裡了,程式碼我都上傳 gitHub 了。

本節完。

參考資料

  • https://developer.android.com/training/testing/espresso?hl=zh-cn

最後附上備忘錄:

espresso-cheatsheet.png