Espresso 测试框架 - 视图匹配器

Espresso 框架提供了许多视图匹配器。匹配器的目的是使用视图的不同属性(如 Id、Text 和子视图的可用性)来匹配视图。每个匹配器匹配视图的特定属性并应用于特定类型的视图。例如,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 测试框架中一些重要的内置匹配器如下 −

任何 - 始终匹配器

基于逻辑的匹配器

  • allOf − 接受任意数量的匹配器,并且只有所有匹配器都成功时才匹配。

  • anyOf − 接受任意数量的匹配器,并且只有任何一个匹配器成功时才匹配。

  • not −接受一个匹配器,并且只有匹配器失败时才匹配,反之亦然。

基于文本的匹配器

  • equalToIgnoringCase − 用于测试实际输入是否等于忽略大小写的预期字符串。

  • equalToIgnoringWhiteSpace − 用于测试实际输入是否等于忽略大小写和空格的指定字符串。

  • containsString − 用于测试实际输入是否包含指定的字符串。

  • endsWith − 用于测试实际输入是否以指定的字符串开头。

  • startsWith −用于测试实际输入是否以指定的字符串结尾。

基于数字的匹配器

  • closeTo − 用于测试实际输入是否接近预期数字。

  • greaterThan − 用于测试实际输入是否大于预期数字。

  • greaterThanOrEqualTo − 用于测试实际输入是否大于或等于预期数字。

  • lessThan − 用于测试实际输入是否小于预期数字。

  • lessThanOrEqualTo −用于测试实际输入是否小于或等于预期数字。

基于对象的匹配器

  • equalTo − 用于测试实际输入是否等于预期对象

  • hasToString − 用于测试实际输入是否具有 toString 方法。

  • instanceOf − 用于测试实际输入是否是预期类的实例。

  • isCompatibleType − 用于测试实际输入是否与预期类型兼容。

  • notNullValue −用于测试实际输入是否不为空。

  • sameInstance − 用于测试实际输入和预期是否是同一个实例。

  • hasProperty − 用于测试实际输入是否具有预期属性

− 是 equalTo 的语法糖或快捷方式>

匹配器

Espresso 提供了 onView() 方法来匹配和查找视图。它接受视图匹配器并返回 ViewInteraction 对象以与匹配的视图进行交互。下面介绍了常用的视图匹配器列表 −

withId()

withId() 接受 int 类型的参数,该参数指的是视图的 id。它返回一个匹配器,该匹配器使用视图的 id 来匹配视图。示例代码如下,

onView(withId(R.id.testView))

withText()

withText() 接受 string 类型的参数,该参数引用视图的 text 属性的值。它返回一个匹配器,该匹配器使用视图的文本值来匹配视图。它仅适用于 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 <Object> 类型的参数,该参数引用视图的标签值。它返回一个匹配器,该匹配器使用其标签值与视图匹配。示例代码如下,

onView(withTagValue(is((Object) "blah")))

此处,is 为 Hamcrest 匹配器。

withClassName()

withClassName() 接受 Matcher<String> 类型的参数,该参数引用视图的类名值。它返回一个匹配器,该匹配器使用其类名与视图匹配。示例代码如下,

onView(withClassName(endsWith("EditText")))

其中,endsWith为Hamcrest匹配器,返回Matcher<String>

withHint()

withHint()接受一个Matcher<String>类型的参数,该参数引用视图的提示值。它返回一个匹配器,该匹配器使用视图的提示与视图进行匹配。示例代码如下,

onView(withClassName(endsWith("Enter name")))

withInputType()

withInputType() 接受一个 int 类型的参数,该参数指的是视图的输入类型。它返回一个匹配器,该匹配器使用其输入类型与视图匹配。示例代码如下,

onView(withInputType(TYPE_CLASS_DATETIME))

此处,TYPE_CLASS_DATETIME 指的是支持日期和时间的编辑视图。

withResourceName()

withResourceName() 接受一个 Matcher<String> 类型的参数,该参数指的是视图的类名值。它返回一个匹配器,该匹配器使用视图的资源名称与视图匹配。示例代码如下,

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<String>的参数,该参数指的是Spinner当前选定视图的值。它返回一个匹配器,根据所选项目的 toString 值匹配微调器。示例代码如下,

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<String>类型的参数,该参数引用视图(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<Integer> 类型的参数,该参数引用视图 (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))))