本文件將說明如何使用 Espresso API。
Espresso API 鼓勵測試作者從使用者可能想看的內容中思考
例如找出 UI 元素並和使用者互動
看似有益或無害的 AI 用途
也可能夾帶問題或相關風險同時,該架構也能防止直接存取活動
都會保留這些物件,
是測試不穩定的主要來源。因此,您
不會在 Espresso API 中看到 getView()
和 getCurrentActivity()
等方法。
您可以實作自己的 子類別,安全地在檢視畫面上執行作業
《ViewAction
》和《ViewAssertion
》。
API 元件
Espresso 的主要元件包括:
- Espresso - 與檢視畫面互動的進入點 (透過
onView()
和onData()
)。此外,也會公開不一定與任何檢視畫面相連結的 API,例如 格式:pressBack()
。 - ViewMatchers - 實作
Matcher<? super View>
介面。您可以將一或多項這類訊息傳送至onView()
方法可在目前的檢視區塊階層中找到檢視區塊。 - ViewActions:可傳遞到的
ViewAction
物件集合ViewInteraction.perform()
方法,例如click()
。 - ViewAssertions - 可自訂的
ViewAssertion
物件集合 已傳遞ViewInteraction.check()
方法。在大多數的情況下,您僅會使用 比對斷言,斷言會使用 View 比對器,宣告 目前所選的資料檢視
例子:
Kotlin
// withId(R.id.my_view) is a ViewMatcher // click() is a ViewAction // matches(isDisplayed()) is a ViewAssertion onView(withId(R.id.my_view)) .perform(click()) .check(matches(isDisplayed()))
Java
// withId(R.id.my_view) is a ViewMatcher // click() is a ViewAction // matches(isDisplayed()) is a ViewAssertion onView(withId(R.id.my_view)) .perform(click()) .check(matches(isDisplayed()));
尋找檢視畫面
在絕大多數情況下,onView()
方法會採用罕見比對器
預期比對項目會與目前檢視畫面中一個項目相符,但只有一個項目
階層比對器的功能很強大,且過去曾使用
透過 Mockito 或 JUnit如果不熟悉「倉鼠比對器」
建議您先看一下這裡
簡報。
一般來說,想要的檢視畫面都有專屬的 R.id
,以及簡單的 withId
比對器會
縮小搜尋範圍然而,在處理工作負載時
無法在測試開發期間判斷 R.id
。舉例來說
沒有 R.id
或 R.id
重複。這可能會
檢測設備測試既簡潔又複雜
無法使用 findViewById()
存取檢視畫面。因此,您或許可以
需要存取容納檢視畫面的活動或片段私人成員,或
找出具有已知 R.id
的容器,然後前往該容器的內容,找出
特定檢視表
Espresso 可讓您縮小檢視畫面範圍,以簡潔的方式處理這個問題
使用現有的 ViewMatcher
物件或您的自訂物件。
透過 R.id
尋找檢視表,就像呼叫 onView()
一樣簡單:
Kotlin
onView(withId(R.id.my_view))
Java
onView(withId(R.id.my_view));
有時候,多個資料檢視會共用 R.id
值。發生這種情況時
嘗試使用特定 R.id
會提供例外狀況,例如
AmbiguousViewMatcherException
。例外狀況訊息會提供
目前檢視區塊階層的示意圖,您可以搜尋及找出
符合重複 R.id
的檢視畫面:
java.lang.RuntimeException: androidx.test.espresso.AmbiguousViewMatcherException This matcher matches multiple views in the hierarchy: (withId: is <123456789>) ... +----->SomeView{id=123456789, res-name=plus_one_standard_ann_button, visibility=VISIBLE, width=523, height=48, has-focus=false, has-focusable=true, window-focus=true, is-focused=false, is-focusable=false, enabled=true, selected=false, is-layout-requested=false, text=, root-is-layout-requested=false, x=0.0, y=625.0, child-count=1} ****MATCHES**** | +------>OtherView{id=123456789, res-name=plus_one_standard_ann_button, visibility=VISIBLE, width=523, height=48, has-focus=false, has-focusable=true, window-focus=true, is-focused=false, is-focusable=true, enabled=true, selected=false, is-layout-requested=false, text=Hello!, root-is-layout-requested=false, x=0.0, y=0.0, child-count=1} ****MATCHES****
檢視各種不同的觀點,應該發現
識別屬性在上述範例中,其中一種檢視畫面含有文字
"Hello!"
。可用於縮小搜尋範圍
比對器:
Kotlin
onView(allOf(withId(R.id.my_view), withText("Hello!")))
Java
onView(allOf(withId(R.id.my_view), withText("Hello!")));
您也可以選擇不反轉任何比對器:
Kotlin
onView(allOf(withId(R.id.my_view), not(withText("Unwanted"))))
Java
onView(allOf(withId(R.id.my_view), not(withText("Unwanted"))));
查看「ViewMatchers
」
一個關於 Espresso 提供的檢視比對器
注意事項
- 在運作良好的應用程式中,使用者可進行互動的所有檢視畫面
應包含描述性文字或內容說明。詳情請見
打造更符合無障礙需求的應用程式
詳細資料。如果無法使用
withText()
或withContentDescription()
,建議將這個問題視為無障礙功能錯誤。 - 使用最少描述性的比對器來尋找所需的檢視畫面
。請勿過度指定,因為這將迫使架構執行超出
。舉例來說,如果檢視畫面的文字能明確識別,您可以
不需要指定檢視畫面也可從
TextView
指派。許多 檢視畫面的R.id
應該就夠用了。 - 如果目標檢視畫面位於
AdapterView
內 (例如ListView
),GridView
或Spinner
:onView()
方法可能無法運作。在以下 則應改用onData()
。
在檢視畫面上執行操作
當您找到了適合目標檢視的比對器後,就可以
使用執行方法,對其執行 ViewAction
的執行個體。
舉例來說,如要點選檢視畫面:
Kotlin
onView(...).perform(click())
Java
onView(...).perform(click());
您可以透過單一執行呼叫來執行多個動作:
Kotlin
onView(...).perform(typeText("Hello"), click())
Java
onView(...).perform(typeText("Hello"), click());
如果您使用的檢視畫面位於 ScrollView
(垂直或
水平) 處理) 並考慮之前需要檢視區塊
透過 scrollTo()
顯示,例如 click()
和 typeText()
。這個
確認檢視畫面會先顯示,再繼續進行其他動作:
Kotlin
onView(...).perform(scrollTo(), click())
Java
onView(...).perform(scrollTo(), click());
查看「ViewActions
」
查看 Espresso 提供的觀看動作
查看檢視畫面斷言
可以使用 check()
,將斷言套用至目前所選的檢視畫面
方法。最常用的斷言是 matches()
斷言。該公式採用
ViewMatcher
物件,用來宣告目前所選檢視畫面的狀態。
例如,如要檢查檢視表是否包含 "Hello!"
文字:
Kotlin
onView(...).check(matches(withText("Hello!")))
Java
onView(...).check(matches(withText("Hello!")));
如要宣告 "Hello!"
是該檢視畫面的內容,則視為不當做法:
Kotlin
// Don't use assertions like withText inside onView. onView(allOf(withId(...), withText("Hello!"))).check(matches(isDisplayed()))
Java
// Don't use assertions like withText inside onView. onView(allOf(withId(...), withText("Hello!"))).check(matches(isDisplayed()));
另一方面,如要宣告含有 "Hello!"
文字的檢視區塊是
可見,例如變更檢視畫面瀏覽權限標記後
沒有問題
查看斷言簡易測試
在這個範例中,SimpleActivity
包含 Button
和 TextView
。當
當使用者按一下按鈕,TextView
的內容就會變更為 "Hello Espresso!"
。
以下說明如何使用 Espresso 進行測試:
點選按鈕
第一步是尋找有助於找到該按鈕的屬性。
如預期,SimpleActivity
中的按鈕有專屬的 R.id
。
Kotlin
onView(withId(R.id.button_simple))
Java
onView(withId(R.id.button_simple));
現在請執行點擊操作:
Kotlin
onView(withId(R.id.button_simple)).perform(click())
Java
onView(withId(R.id.button_simple)).perform(click());
驗證 TextView 文字
用於驗證文字的 TextView
也具有專屬的 R.id
:
Kotlin
onView(withId(R.id.text_simple))
Java
onView(withId(R.id.text_simple));
現在若要驗證內容文字:
Kotlin
onView(withId(R.id.text_simple)).check(matches(withText("Hello Espresso!")))
Java
onView(withId(R.id.text_simple)).check(matches(withText("Hello Espresso!")));
在轉接程式檢視畫面中檢查資料載入
AdapterView
是一種特殊類型的小工具,可以從
轉接器AdapterView
最常見的例子是 ListView
。阿斯
與 LinearLayout
等靜態小工具不同,
AdapterView
子項可載入目前的檢視區塊階層。簡單
onView()
搜尋找不到目前未載入的檢視畫面。
Espresso 會透過提供獨立的 onData()
進入點來進行處理
必須先載入有問題的轉接器項目,然後在
對它或其任何子項執行的作業。
警告:
AdapterView
可能發生與「onData()
」有關的問題
方法,尤其是
getItem()
API。在這種情況下,最佳做法是
重構應用程式程式碼如果無法這麼做,可以
符合自訂 AdapterViewProtocol
。如需更多資訊,請
看看預設的
AdapterViewProtocols
類別 (由 Espresso 提供)。
轉接程式檢視畫面簡易測試
這個簡單的測試示範如何使用 onData()
。SimpleActivity
包含
Spinner
,其中幾個項目代表咖啡飲料類型。如果
已選取項目,就會有 TextView
變更為 "One %s a day!"
,其中
%s
表示所選項目。
這項測試的目標在於開啟 Spinner
並選取特定項目,然後
確認 TextView
包含項目。由於 Spinner
類別是以
在 AdapterView
,建議使用 onData()
,而不是 onView()
:
符合項目。
開啟項目選項
Kotlin
onView(withId(R.id.spinner_simple)).perform(click())
Java
onView(withId(R.id.spinner_simple)).perform(click());
選取項目
針對選取項目,Spinner
會建立含有其內容的 ListView
。
這個檢視畫面可能很長,且該元素可能無法為檢視畫面貢獻內容
階層使用 onData()
時,我們會將所需元素強制入檢視畫面中
階層Spinner
中的項目是字串,因此我們想比對一個項目
等於 String "Americano"
:
Kotlin
onData(allOf(`is`(instanceOf(String::class.java)), `is`("Americano"))).perform(click())
Java
onData(allOf(is(instanceOf(String.class)), is("Americano"))).perform(click());
確認文字是否正確
Kotlin
onView(withId(R.id.spinnertext_simple)) .check(matches(withText(containsString("Americano"))))
Java
onView(withId(R.id.spinnertext_simple)) .check(matches(withText(containsString("Americano"))));
偵錯
當測試失敗時,Espresso 會提供實用的偵錯資訊:
記錄
Espresso 會將所有檢視動作記錄到 logcat。例如:
ViewInteraction: Performing 'single click' action on view with text: Espresso
檢視區塊階層
執行 onView()
時,Espresso 會在例外狀況訊息中顯示檢視區塊階層
失敗。
- 如果
onView()
找不到目標檢視畫面,NoMatchingViewException
是 擲回。您可以在例外狀況字串中查看檢視區塊階層,以進行分析 為何比對器與任何檢視畫面不相符? - 如果
onView()
發現多個符合指定比對器的檢視畫面, 已擲回AmbiguousViewMatcherException
。系統會顯示檢視區塊階層 相符的資料檢視會標上MATCHES
標籤:
java.lang.RuntimeException: androidx.test.espresso.AmbiguousViewMatcherException This matcher matches multiple views in the hierarchy: (withId: is <123456789>) ... +----->SomeView{id=123456789, res-name=plus_one_standard_ann_button, visibility=VISIBLE, width=523, height=48, has-focus=false, has-focusable=true, window-focus=true, is-focused=false, is-focusable=false, enabled=true, selected=false, is-layout-requested=false, text=, root-is-layout-requested=false, x=0.0, y=625.0, child-count=1} ****MATCHES**** | +------>OtherView{id=123456789, res-name=plus_one_standard_ann_button, visibility=VISIBLE, width=523, height=48, has-focus=false, has-focusable=true, window-focus=true, is-focused=false, is-focusable=true, enabled=true, selected=false, is-layout-requested=false, text=Hello!, root-is-layout-requested=false, x=0.0, y=0.0, child-count=1} ****MATCHES****
處理複雜的檢視區塊階層或小工具非預期的行為時 使用 Android Studio 中的階層檢視器: 解釋
轉接程式檢視畫面警告
Espresso 會警告使用者有 AdapterView
小工具,出現onView()
時
作業會擲回 NoMatchingViewException
和 AdapterView
小工具
中,最常見的解決方案是使用 onData()
。
例外狀況訊息會包含內含轉接器檢視畫面清單的警告。
您可以使用這項資訊叫用 onData()
載入目標檢視畫面。
其他資源
如要進一步瞭解如何在 Android 測試中使用 Espresso,請參閱 資源。
範例
- CustomMatcherSample:
說明如何擴充 Espresso,以符合
EditText
物件的提示屬性。 - RecyclerViewSample:
RecyclerView
動作適用於 Espresso。 - (更多...)