Espresso 测试框架 - 视图匹配器


Espresso 框架提供了许多视图匹配器。匹配器的目的是使用视图的不同属性(例如 ID、文本和子视图的可用性)来匹配视图。每个匹配器匹配视图的特定属性并应用于特定类型的视图。例如,withId匹配器匹配视图的Id属性并适用于所有视图,而 withText 匹配器匹配视图的Text属性并仅适用于TextView

在本章中,让我们学习 espresso 测试框架提供的不同匹配器,并了解构建 espresso 匹配器的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 - 用于测试实际输入是否具有预期属性

is - 糖或equalTo的捷径

匹配器

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

withId()

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

onView(withId(R.id.testView))

withText()

withText()接受字符串类型的参数,该参数引用视图的文本属性的值。它返回一个匹配器,该匹配器使用视图的文本值来匹配视图。它仅适用于TextView。示例代码如下,

onView(withText("Hello World!"))

withContentDescription()

withContentDescription()接受字符串类型的参数,该参数引用视图的内容描述属性的值。它返回一个匹配器,该匹配器使用视图的描述来匹配视图。示例代码如下,

onView(withContentDescription("blah"))

我们还可以传递文本值的资源 ID,而不是文本本身。

onView(withContentDescription(R.id.res_id_blah))

有内容描述()

hasContentDescription()没有参数。它返回一个匹配器,该匹配器与具有任何内容描述的视图相匹配。示例代码如下,

onView(allOf(withId(R.id.my_view_id), hasContentDescription()))

withTagKey()

withTagKey()接受字符串类型的参数,该参数引用视图的标签键。它返回一个匹配器,该匹配器使用其标签键来匹配视图。示例代码如下,

onView(withTagKey("blah"))

我们还可以传递标签名称的资源 ID,而不是标签名称本身。

onView(withTagKey(R.id.res_id_blah))

withTagValue()

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

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

这里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()没有参数,它返回一个匹配器,该匹配器与具有链接的视图相匹配。它仅适用于 TextView。示例代码如下,

onView(allOf(withSubString("Hello"), hasLinks()))

在这里,allOf是 Hamcrest 匹配器。allOf返回一个匹配器,它匹配所有传入的匹配器,在这里,它用于匹配视图以及检查视图的文本值中是否有链接。

有文本颜色()

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()没有参数。它返回一个匹配器,该匹配器与具有任何多行文本的 TextView 匹配。示例代码如下,

onView(allOf(withId(R.id.my_test_view_id), hasMultilineText()))

有背景()

hasBackground()接受一个 int 类型的参数,该参数引用后台资源的资源 id。它返回一个匹配器,该匹配器根据视图的背景资源来匹配视图。示例代码如下,

onView(allOf(withId("image"), hasBackground(R.drawable.your_drawable)))

有错误文本()

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()没有参数。它返回一个匹配器,如果视图支持输入法,则该匹配器与视图相匹配。示例代码如下,

onView(allOf(withId(R.id.editText_name), supportsInputMethods()))

是根()

isRoot()没有参数。它返回一个与根视图匹配的匹配器。示例代码如下,

onView(allOf(withId(R.id.my_root_id), isRoot()))

被展示()

isDisplayed()没有参数。它返回一个匹配器,该匹配器与当前显示的视图相匹配。示例代码如下,

onView(allOf(withId(R.id.my_view_id), isDisplayed()))

isDisplayingAtLeast()

isDisplayingAtLeast()接受一个 int 类型的参数。它返回一个匹配器,该匹配器与当前显示的视图至少匹配指定的百分比。示例代码如下,

onView(allOf(withId(R.id.my_view_id), isDisplayingAtLeast(75)))

完全显示()

isCompletelyDisplayed()没有参数。它返回一个匹配器,该匹配器与当前完全显示在屏幕上的视图相匹配。示例代码如下,

onView(allOf(withId(R.id.my_view_id), isCompletelyDisplayed()))

已启用()

isEnabled()没有参数。它返回一个匹配器,该匹配器与启用的视图相匹配。示例代码如下,

onView(allOf(withId(R.id.my_view_id), isEnabled()))

isFocusable()

isFocusable()没有参数。它返回一个匹配器,该匹配器与具有焦点选项的视图相匹配。示例代码如下,

onView(allOf(withId(R.id.my_view_id), isFocusable()))

有焦点()

hasFocus()没有参数。它返回一个匹配器,该匹配器与当前聚焦的视图相匹配。示例代码如下,

onView(allOf(withId(R.id.my_view_id), hasFocus()))

可点击()

isClickable()没有参数。它返回一个匹配器,该匹配器与单击选项的视图相匹配。示例代码如下,

onView(allOf(withId(R.id.my_view_id), isClickable()))

isSelected()

isSelected()没有参数。它返回一个匹配器,该匹配器与当前选择的视图相匹配。示例代码如下,

onView(allOf(withId(R.id.my_view_id), isSelected()))

已检查()

isChecked()没有参数。它返回一个匹配器,该匹配器与复合按钮类型(或其子类型)且处于选中状态的视图相匹配。示例代码如下,

onView(allOf(withId(R.id.my_view_id), isChecked()))

未检查()

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()接受一个类型为 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()接受一个 Matcher<View> 类型的参数。论证指的是一种观点。它返回一个匹配器,该匹配器匹配传入视图是视图层次结构中的后代视图之一的视图。示例代码如下,

onView(hasDescendant(withId(R.id.descendantView)))

isDescendantOfA()

isDescendantOfA()接受一个 Matcher<View> 类型的参数。论证指的是一种观点。它返回一个匹配器,该匹配器匹配传入视图是视图层次结构中的祖先视图之一的视图。示例代码如下,

onView(allOf(withId(R.id.myView), isDescendantOfA(withId(R.id.parentView))))