- Espresso 測試框架教程
- Espresso 測試 - 首頁
- 簡介
- 設定說明
- 在 Android Studio 中執行測試
- JUnit 概述
- 架構
- 檢視匹配器
- 自定義檢視匹配器
- 檢視斷言
- 檢視操作
- 測試 AdapterView
- 測試 WebView
- 測試非同步操作
- 測試 Intent
- 測試多個應用程式的 UI
- 測試錄製器
- 測試 UI 效能
- 測試可訪問性
- Espresso 測試資源
- Espresso 測試 - 快速指南
- Espresso 測試 - 有用資源
- Espresso 測試 - 討論
Espresso 測試框架 - 檢視匹配器
Espresso 框架提供了許多檢視匹配器。匹配器的目的是使用檢視的不同屬性(例如 Id、文字和子檢視的可用性)來匹配檢視。每個匹配器都匹配檢視的特定屬性並應用於特定型別的檢視。例如,withId 匹配器匹配檢視的 Id 屬性並應用於所有檢視,而 withText 匹配器匹配檢視的 Text 屬性,僅應用於 TextView。
在本章中,讓我們學習 Espresso 測試框架提供的不同匹配器,以及學習 Espresso 匹配器構建的基礎庫 Hamcrest。
Hamcrest 庫
Hamcrest 庫在 Espresso 測試框架的範圍內是一個重要的庫。Hamcrest 本身是一個用於編寫匹配器物件的框架。Espresso 框架廣泛使用 Hamcrest 庫,並在必要時對其進行擴充套件以提供簡單且可擴充套件的匹配器。
Hamcrest 提供了一個簡單的函式 assertThat 和一系列匹配器來斷言任何物件。assertThat 有三個引數,如下所示:
字串(測試描述,可選)
物件(實際值)
匹配器(預期值)
讓我們編寫一個簡單的示例來測試列表物件是否具有預期值。
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.MatcherAssert.assertThat;
@Test
public void list_hasValue() {
ArrayList<String> list = new ArrayList<String>();
list.add("John");
assertThat("Is list has John?", list, hasItem("John"));
}
這裡,hasItem 返回一個匹配器,它檢查實際列表是否具有指定值作為其中一項。
Hamcrest 具有許多內建匹配器,以及建立新匹配器的選項。在 Espresso 測試框架中,一些有用的重要內建匹配器如下:
anything - 總是匹配
基於邏輯的匹配器
allOf - 接受任意數量的匹配器,只有當所有匹配器都成功時才匹配。
anyOf - 接受任意數量的匹配器,如果任何一個匹配器成功則匹配。
not - 接受一個匹配器,只有當匹配器失敗時才匹配,反之亦然。
基於文字的匹配器
equalToIgnoringCase - 用於測試實際輸入是否等於預期字串(忽略大小寫)。
equalToIgnoringWhiteSpace - 用於測試實際輸入是否等於指定的字串(忽略大小寫和空格)。
containsString - 用於測試實際輸入是否包含指定的字串。
endsWith - 用於測試實際輸入是否以指定的字串結尾。
startsWith - 用於測試實際輸入是否以指定的字串開頭。
基於數字的匹配器
closeTo - 用於測試實際輸入是否接近預期數字。
greaterThan - 用於測試實際輸入是否大於預期數字。
greaterThanOrEqualTo - 用於測試實際輸入是否大於或等於預期數字。
lessThan - 用於測試實際輸入是否小於預期數字。
lessThanOrEqualTo - 用於測試實際輸入是否小於或等於預期數字。
基於物件的匹配器
equalTo - 用於測試實際輸入是否等於預期物件。
hasToString - 用於測試實際輸入是否具有 toString 方法。
instanceOf - 用於測試實際輸入是否是預期類的例項。
isCompatibleType - 用於測試實際輸入是否與預期型別相容。
notNullValue - 用於測試實際輸入是否不為空。
sameInstance - 用於測試實際輸入和預期值是否為同一例項。
hasProperty - 用於測試實際輸入是否具有預期的屬性。
is - equalTo 的簡寫或語法糖
匹配器
Espresso 提供了 onView() 方法來匹配和查詢檢視。它接受檢視匹配器並返回 ViewInteraction 物件以與匹配的檢視互動。常用檢視匹配器列表如下:
withId()
withId() 接受一個 int 型別的引數,該引數引用檢視的 id。它返回一個匹配器,該匹配器使用檢視的 id 來匹配檢視。示例程式碼如下:
onView(withId(R.id.testView))
withText()
withText() 接受一個 string 型別的引數,該引數引用檢視的文字屬性的值。它返回一個匹配器,該匹配器使用檢視的文字值來匹配檢視。它僅適用於 TextView。示例程式碼如下:
onView(withText("Hello World!"))
withContentDescription()
withContentDescription() 接受一個 string 型別的引數,該引數引用檢視的內容描述屬性的值。它返回一個匹配器,該匹配器使用檢視的描述來匹配檢視。示例程式碼如下:
onView(withContentDescription("blah"))
我們還可以傳遞文字值的資源 ID 而不是文字本身。
onView(withContentDescription(R.id.res_id_blah))
hasContentDescription()
hasContentDescription() 沒有引數。它返回一個匹配器,該匹配器匹配具有任何內容描述的檢視。示例程式碼如下:
onView(allOf(withId(R.id.my_view_id), hasContentDescription()))
withTagKey()
withTagKey() 接受一個 string 型別的引數,該引數引用檢視的標籤鍵。它返回一個匹配器,該匹配器使用其標籤鍵來匹配檢視。示例程式碼如下:
onView(withTagKey("blah"))
我們還可以傳遞標籤名稱的資源 ID 而不是標籤名稱本身。
onView(withTagKey(R.id.res_id_blah))
withTagValue()
withTagValue() 接受一個 Matcher 型別的引數,該引數引用檢視的標籤值。它返回一個匹配器,該匹配器使用其標籤值來匹配檢視。示例程式碼如下:
onView(withTagValue(is((Object) "blah")))
這裡,is 是 Hamcrest 匹配器。
withClassName()
withClassName() 接受一個 Matcher 型別的引數,該引數引用檢視的類名值。它返回一個匹配器,該匹配器使用其類名來匹配檢視。示例程式碼如下:
onView(withClassName(endsWith("EditText")))
這裡,endsWith 是 Hamcrest 匹配器並返回 Matcher。
withHint()
withHint() 接受一個 Matcher 型別的引數,該引數引用檢視的提示值。它返回一個匹配器,該匹配器使用檢視的提示來匹配檢視。示例程式碼如下:
onView(withClassName(endsWith("Enter name")))
withInputType()
withInputType() 接受一個 int 型別的引數,該引數引用檢視的輸入型別。它返回一個匹配器,該匹配器使用其輸入型別來匹配檢視。示例程式碼如下:
onView(withInputType(TYPE_CLASS_DATETIME))
這裡,TYPE_CLASS_DATETIME 指的是支援日期和時間的編輯檢視。
withResourceName()
withResourceName() 接受一個 Matcher 型別的引數,該引數引用檢視的類名值。它返回一個匹配器,該匹配器使用檢視的資源名稱來匹配檢視。示例程式碼如下:
onView(withResourceName(endsWith("res_name")))
它也接受字串引數。示例程式碼如下:
onView(withResourceName("my_res_name"))
withAlpha()
withAlpha() 接受一個 float 型別的引數,該引數引用檢視的 alpha 值。它返回一個匹配器,該匹配器使用檢視的 alpha 值來匹配檢視。示例程式碼如下:
onView(withAlpha(0.8))
withEffectiveVisibility()
withEffectiveVisibility() 接受一個 ViewMatchers.Visibility 型別的引數,該引數引用檢視的有效可見性。它返回一個匹配器,該匹配器使用檢視的可見性來匹配檢視。示例程式碼如下:
onView(withEffectiveVisibility(withEffectiveVisibility.INVISIBLE))
withSpinnerText()
withSpinnerText() 接受一個 Matcher 型別的引數,該引數引用 Spinner 當前選定檢視的值。它返回一個匹配器,該匹配器根據其選定專案的 toString 值來匹配 Spinner。示例程式碼如下:
onView(withSpinnerText(endsWith("USA")))
它也接受字串引數或字串的資源 ID。示例程式碼如下:
onView(withResourceName("USA"))
onView(withResourceName(R.string.res_usa))
withSubstring()
withSubString() 與 withText() 類似,不同之處在於它有助於測試檢視文字值的子字串。
onView(withSubString("Hello"))
hasLinks()
hasLinks() 沒有引數,它返回一個匹配器,該匹配器匹配具有連結的檢視。它僅適用於 TextView。示例程式碼如下:
onView(allOf(withSubString("Hello"), hasLinks()))
這裡,allOf 是一個 Hamcrest 匹配器。allOf 返回一個匹配器,該匹配器匹配所有傳入的匹配器,在這裡,它用於匹配檢視以及檢查檢視的文字值中是否包含連結。
hasTextColor()
hasTextColor() 接受一個 int 型別的引數,該引數引用顏色的資源 ID。它返回一個匹配器,該匹配器根據其顏色匹配 TextView。它僅適用於 TextView。示例程式碼如下:
onView(allOf(withSubString("Hello"), hasTextColor(R.color.Red)))
hasEllipsizedText()
hasEllipsizedText() 沒有引數。它返回一個匹配器,該匹配器匹配具有長文字且已省略號顯示(開頭…中間…結尾)或被截斷(開頭…)的 TextView。示例程式碼如下:
onView(allOf(withId(R.id.my_text_view_id), hasEllipsizedText()))
hasMultilineText()
hasMultilineText() 沒有引數。它返回一個匹配器,該匹配器匹配具有任何多行文字的 TextView。示例程式碼如下:
onView(allOf(withId(R.id.my_test_view_id), hasMultilineText()))
hasBackground()
hasBackground() 接受一個 int 型別的引數,該引數引用背景資源的資源 ID。它返回一個匹配器,該匹配器根據其背景資源匹配檢視。示例程式碼如下:
onView(allOf(withId("image"), hasBackground(R.drawable.your_drawable)))
hasErrorText()
hasErrorText() 接受一個 Matcher 型別的引數,該引數引用檢視(EditText)的錯誤字串值。它返回一個匹配器,該匹配器使用檢視的錯誤字串來匹配檢視。這僅適用於 EditText。示例程式碼如下:
onView(allOf(withId(R.id.editText_name), hasErrorText(is("name is required"))))
它也接受字串引數。示例程式碼如下:
onView(allOf(withId(R.id.editText_name), hasErrorText("name is required")))
hasImeAction()
hasImeAction() 接受一個 Matcher 型別的引數,該引數引用檢視(EditText)支援的輸入方法。它返回一個匹配器,該匹配器使用檢視支援的輸入方法來匹配檢視。這僅適用於 EditText。示例程式碼如下:
onView(allOf(withId(R.id.editText_name), hasImeAction(is(EditorInfo.IME_ACTION_GO))))
這裡,EditorInfo.IME_ACTION_GO 是輸入方法選項之一。hasImeAction() 也接受整數引數。示例程式碼如下:
onView(allOf(withId(R.id.editText_name), hasImeAction(EditorInfo.IME_ACTION_GO)))
supportsInputMethods()
supportsInputMethods() 沒有引數。如果檢視支援輸入方法,它將返回一個匹配該檢視的匹配器。示例程式碼如下:
onView(allOf(withId(R.id.editText_name), supportsInputMethods()))
isRoot()
isRoot() 沒有引數。它返回一個匹配根檢視的匹配器。示例程式碼如下:
onView(allOf(withId(R.id.my_root_id), isRoot()))
isDisplayed()
isDisplayed() 沒有引數。它返回一個匹配當前顯示的檢視的匹配器。示例程式碼如下:
onView(allOf(withId(R.id.my_view_id), isDisplayed()))
isDisplayingAtLeast()
isDisplayingAtLeast() 接受一個 int 型別的引數。它返回一個匹配器,該匹配器匹配當前至少顯示指定百分比的檢視。示例程式碼如下:
onView(allOf(withId(R.id.my_view_id), isDisplayingAtLeast(75)))
isCompletelyDisplayed()
isCompletelyDisplayed() 沒有引數。它返回一個匹配器,該匹配器匹配當前完全顯示在螢幕上的檢視。示例程式碼如下:
onView(allOf(withId(R.id.my_view_id), isCompletelyDisplayed()))
isEnabled()
isEnabled() 沒有引數。它返回一個匹配已啟用檢視的匹配器。示例程式碼如下:
onView(allOf(withId(R.id.my_view_id), isEnabled()))
isFocusable()
isFocusable() 沒有引數。它返回一個匹配具有焦點選項的檢視的匹配器。示例程式碼如下:
onView(allOf(withId(R.id.my_view_id), isFocusable()))
hasFocus()
hasFocus() 沒有引數。它返回一個匹配器,匹配當前獲得焦點的檢視。示例程式碼如下:
onView(allOf(withId(R.id.my_view_id), hasFocus()))
isClickable()
isClickable() 沒有引數。它返回一個匹配器,匹配具有點選選項的檢視。示例程式碼如下:
onView(allOf(withId(R.id.my_view_id), isClickable()))
isSelected()
isSelected() 沒有引數。它返回一個匹配器,匹配當前選中的檢視。示例程式碼如下:
onView(allOf(withId(R.id.my_view_id), isSelected()))
isChecked()
isChecked() 沒有引數。它返回一個匹配器,匹配型別為CompoundButton(或其子型別)且處於選中狀態的檢視。示例程式碼如下:
onView(allOf(withId(R.id.my_view_id), isChecked()))
isNotChecked()
isNotChecked() 與isChecked正好相反。示例程式碼如下:
onView(allOf(withId(R.id.my_view_id), isNotChecked()))
isJavascriptEnabled()
isJavascriptEnabled() 沒有引數。它返回一個匹配器,匹配正在執行JavaScript的WebView。示例程式碼如下:
onView(allOf(withId(R.id.my_webview_id), isJavascriptEnabled()))
withParent()
withParent() 接受一個型別為Matcher<View>的引數。該引數指的是一個檢視。它返回一個匹配器,匹配指定檢視作為父檢視的檢視。示例程式碼如下:
onView(allOf(withId(R.id.childView), withParent(withId(R.id.parentView))))
hasSibling()
hasSibling() 接受一個型別為Matcher<View>的引數。該引數指的是一個檢視。它返回一個匹配器,匹配傳入檢視是其兄弟檢視之一的檢視。示例程式碼如下:
onView(hasSibling(withId(R.id.siblingView)))
withChild()
withChild() 接受一個型別為Matcher<View>的引數。該引數指的是一個檢視。它返回一個匹配器,匹配傳入檢視是子檢視的檢視。示例程式碼如下:
onView(allOf(withId(R.id.parentView), withChild(withId(R.id.childView))))
hasChildCount()
hasChildCount() 接受一個型別為int的引數。該引數指的是檢視的子檢視數量。它返回一個匹配器,匹配子檢視數量與引數中指定的數量完全相同的檢視。示例程式碼如下:
onView(hasChildCount(4))
hasMinimumChildCount()
hasMinimumChildCount() 接受一個型別為int的引數。該引數指的是檢視的子檢視數量。它返回一個匹配器,匹配子檢視數量至少與引數中指定的數量相同的檢視。示例程式碼如下:
onView(hasMinimumChildCount(4))
hasDescendant()
hasDescendant() 接受一個型別為Matcher<View>的引數。該引數指的是一個檢視。它返回一個匹配器,匹配傳入檢視是檢視層次結構中某個後代檢視的檢視。示例程式碼如下:
onView(hasDescendant(withId(R.id.descendantView)))
isDescendantOfA()
isDescendantOfA() 接受一個型別為Matcher<View>的引數。該引數指的是一個檢視。它返回一個匹配器,匹配傳入檢視是檢視層次結構中某個祖先檢視的檢視。示例程式碼如下:
onView(allOf(withId(R.id.myView), isDescendantOfA(withId(R.id.parentView))))