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))))