【Android测试】【第十九节】Espresso——API详解
◆版权声明:本文出自胖喵~的博客,转载必须注明出处。
转载请注明出处:http://www.cnblogs.com/by-dream/p/5997557.html
前言
Espresso的提供了不少API支持使用者来和界面元素进行交互,但同时它又阻止使用者直接获取Activity和View,它为的就是想保持让这些对象在UI线程中执行,以防发生线程不安全的情况。因此在Espresso中我们看不到getView、getCurrentActivity类似这样的方法。但是我们可以通过实行自己的ViewAction和ViewAssertion来安全的操作View。这就是Espresso的思想。
认识组件
Espresso:与视图交互的入口(通过onView和onData),还包含一些不绑定到任何元素上的API(例如pressBack)。
ViewMatchers: 实现Matcher<? super View>接口对象的集合,可以将一个或者多个传递给onView,从而定位当前视图中的元素。
ViewActions:可以传递到ViewInteraction.perform方法的集合(例如click)。
ViewAssertions:可以传递到ViewInteraction.check方法的集合。 大多数时候需要matches断言,即使用View Matcher来和当前选择的视图的状态进行断言。
举例:

定位元素onView
onView使用的是一个hamcrest匹配器,该匹配器只匹配当前视图层次结构中的一个(且只有一个)视图。如果你不熟悉hamcrest匹配器,建议先看看这个。通常情况下一个控件的id是唯一的,但是有些特定的视图是无法通过R.id拿到,因此我们就需要访问Activity或者Fragment的私有成员找到拥有R.id的容器。有的时候也需要使用ViewMatchers来缩小定位的范围。
最简单的onView就是这样的形式:
onView(withId(R.id.my_view))
有的时候多个视图之间共享R.id值,当这种情况下,我们调用系统会抛出这样的异常AmbiguousViewMatcherException:
java.lang.RuntimeException:
com.google.android.apps.common.testing.ui.espresso.AmbiguousViewMatcherException:
This matcher matches multiple views in the hierarchy: (withId: is <>)
当然系统给出你详细的信息,让你进行排查:
+----->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****
通过上面的信息对比,我们可以发现text字段是不一致的,因此我们就可以根据这个组合匹配来缩小定位范围,方法如下:
onView(allOf(withId(R.id.my_view), withText("Hello!")))
你也可以使用这样的方法:
onView(allOf(withId(R.id.my_view), not(withText("Unwanted"))))
对于大部分的控件,使用上述的方法就可以搞定了,如果你发现使用“withText”或“withContentDescription”都无法定位到元素的时候,谷歌建议你可以给开发提一个可访问性的bug了。
下面这种情况,出现了很多同样的数字,但是它旁边有可以识别出的唯一元素,这个时候我们就也可以使用hasSibling来进行筛选:

onView(allOf(withText("7"), hasSibling(withText("item: 0")))).perform(click());
另外说两个常用的menu,如果是 overflow menu也就是下面这种情况的下:

需要使用:
openActionBarOverflowOrOptionsMenu(getInstrumentation().getTargetContext());
如果是下面这样的:

使用:
openContextualActionModeOverflowMenu();
注意:如果目标视图在AdapterView(例如ListView,GridView,Spinner)中,onView方法可能无法正常工作,这个时候需要用到onData方法。
定位元素onData
假设一个Spinner的控件,我们要点击“Americano”,我们使用默认的Adaptor,它的字段默认是String的,因此当我们要进行点击的时候,就可以使用如下方法:
onData(allOf(is(instanceOf(String.class)), is("Americano"))).perform(click());
假设是一个Listview,我们需要点击Listview中第二个item的按钮,那么我们需要这样写:
onData(Matchers.allOf())
.inAdapterView(withId(R.id.photo_gridview)) // listview的id
.atPosition(1) // 所在位置
.onChildView(withId(R.id.imageview_photo)) // item中子控件id
.perform(click());
执行操作
当你获取到了目标的控件后,就可以使用perform来执行操作了。例如一个点击操作:
onView(...).perform(click());
也可以通过一个命令执行多个操作:
// 输入hello,并且点击
onView(...).perform(typeText("Hello"), click());
当你操作的对象如果是ScrollView,在执行其他操作(例如click、typeText)之前,必须确保当前的控件是出现在当前的可视范围的,若没有可以使用scrollTo的方法:
onView(...).perform(scrollTo(), click());
如果可视范围已经出现了该元素,scrollTo将不起作用,因此当你的屏幕分辨率大或小的时候,都可以放心安全地使用它。
校验
使用check方法可以断言当前选择的界面, 常用的断言是matches,它使用ViewMatcher来断言当前选定视图的状态。例如,要检查视图中是否包含“Hello”这个字符串:
onView(...).check(matches(withText("Hello")));
千万不要使用下面这样的方法做断言,谷歌是不推荐这样的:
// Don't use assertions like withText inside onView.
onView(allOf(withId(...), withText("Hello!"))).check(matches(isDisplayed()));
所以当我们需要断言一个指定的内容是否在AdapterView当中的时候,我们需要做一些特殊的处理。做法就是找到AdapterView,然后访问它的内部元素,这里不适用onData,而是使用onView和我们自己写的matcher来进行处理。我们自定义一个matcher叫withAdaptedData,实现如下:
private static Matcher<View> withAdaptedData(final Matcher<Object> dataMatcher) {
return new TypeSafeMatcher<View>() {
@Override
public void describeTo(Description description) {
description.appendText("with class name: ");
dataMatcher.describeTo(description);
}
@Override
public boolean matchesSafely(View view) {
if (!(view instanceof AdapterView)) {
return false;
}
@SuppressWarnings("rawtypes")
Adapter adapter = ((AdapterView) view).getAdapter();
for (int i = 0; i < adapter.getCount(); i++) {
if (dataMatcher.matches(adapter.getItem(i))) {
return true;
}
}
return false;
}
};
}
然后我们就可以使用它来进行断言了:
// 当list当中是否存在一个bryan的item,就断言失败
onView(withId(R.id.list)).check(matches(not(withAdaptedData(withItemContent("bryan")))));
因为里面还用到了一个withItemContent,我们也需要实现它:
public static Matcher<Object> withItemContent(final Matcher<String> itemTextMatcher) {
// use preconditions to fail fast when a test is creating an invalid matcher.
checkNotNull(itemTextMatcher);
return new BoundedMatcher<Object, Map>(Map.class) {
@Override
public boolean matchesSafely(Map map) {
return hasEntry(equalTo("STR"), itemTextMatcher).matches(map);
}
@Override
public void describeTo(Description description) {
description.appendText("with item content: ");
itemTextMatcher.describeTo(description);
}
};
}
所以当我们要断言时,如果遇到了一些没有实现的内容,就需要我们重写matcher了。
参考图
下面这幅图,我们在写代码中可以快速查看,这里面包含了大部分我们经常用的API:

细心的人应该能看到图中有intent相关的内容,这部分内容我目前没有用到,因此也没有深入的了解。有兴趣的可以自己看看。
参考链接:https://google.github.io/android-testing-support-library/docs/espresso/intents/index.html
【Android测试】【第十九节】Espresso——API详解的更多相关文章
- “全栈2019”Java多线程第十九章:死锁详解
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...
- “全栈2019”Java异常第十九章:RuntimeException详解
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java异 ...
- “全栈2019”Java第二十九章:数组详解(中篇)
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...
- Android Developer -- Bluetooth篇 开发实例之四 API详解
http://www.open-open.com/lib/view/open1390879771695.html 这篇文章将会详细解析BluetoothAdapter的详细api, 包括隐藏方法, 每 ...
- Android 音视频开发(六): MediaCodec API 详解
在学习了Android 音视频的基本的相关知识,并整理了相关的API之后,我们应该对基本的音视频有一定的轮廓了. 下面开始接触一个Android音视频中相当重要的一个API: MediaCodec.通 ...
- 第三十九课:requestAnimationFrame详解
大家应该都知道,如果一个页面运行的定时器很多,无论你怎么优化,最后肯定会超过指定时间才能完成动画.定时器越多,延时越严重. 为此,YUI,kissy等采用中央队列的方式,将定时器减少至一个.浏览器厂商 ...
- jQuery 源码分析(十九) DOM遍历模块详解
jQuery的DOM遍历模块对DOM模型的原生属性parentNode.childNodes.firstChild.lastChild.previousSibling.nextSibling进行了封装 ...
- 大白话5分钟带你走进人工智能-第二十九节集成学习之随机森林随机方式 ,out of bag data及代码(2)
大白话5分钟带你走进人工智能-第二十九节集成学习之随机森林随机方式 ,out of bag data及代码(2) 上一节中我们讲解了随机森林的基本概念,本节的话我们讲解随机森 ...
- 第三百二十九节,web爬虫讲解2—urllib库爬虫—ip代理—用户代理和ip代理结合应用
第三百二十九节,web爬虫讲解2—urllib库爬虫—ip代理 使用IP代理 ProxyHandler()格式化IP,第一个参数,请求目标可能是http或者https,对应设置build_opener ...
随机推荐
- nginx+webpy配置
之前搞app时候学的webpy,一直用的自带webserver,最近研究nginx一段时间,决定二者结合玩一下~ 把搭建的要点总结下,说不定哪天还得用——其实平时手挺懒的... 1 必备模块和背景知识 ...
- Other Linker Flags到底是什么
一.问题描述 在项目开发中用到百度地图,有时候在工程中会报“方法找不到”的错误(unrecognized selector sent to instance). 二.问题分析 首先,要说明一下Othe ...
- .NET 接口
接口 接口是一组抽象成员的集合,表示某个类或结构可以选择去实现的行为,描述的是可属于任何类或结构的一组相关功能.接口方法的实现是在实现接口的类中完成的,实现接口的类可以显式实现该接口的成员, ...
- scrollView的讲解
今天就讲下UIScrollView的一些事情,这个可以拖动的组件无论在应用还是游戏开发都会经常用到,所以我们就一定要更加熟悉它了.下面我们开始下手咯. (1)初始化 一般的组件初始化都可以alloc和 ...
- IOS处理点空白处不自动失去焦点的问题
objBlurFun("input"); //如果不是当前触摸点不在input上,那么都失去焦点 function objBlurFun(sDom,time){ var time ...
- Hadoop Linux安装
Hadoop Linux安装 步骤流程 1.硬件准备 2.软件准备(推荐CDH) 3.将Hadoop安装包分发到各个节点下 4.安装JDK 5.修改/etc/hosts配置文件 6.设置SSH免密码登 ...
- DS28E01芯片解密DS28E01-100单片机解密多少钱?
DS28E01芯片解密DS28E01-100单片机解密多少钱? DS28E01-100将1024位EEPROM与符合ISO/IEC 10118-3安全散列算法(SHA-1)的质询响应安全认证结合在一起 ...
- WPF 点击Calendar后,需要点击两次按钮
主面板上有一个Calendar控件,点击选择了日期后,如果点击确认按钮,需要点击两次.这个问题的解决方法如下: private void calendar1_PreviewMouseUp(ob ...
- 【Linux】vi 命令
基本上 vi/vim 共分为三种模式,分别是一般模式.编辑模式与指令列命令模式. 这三种模式的作用分别是: 一般模式:以 vi 打开一个档案就直接进入一般模式了(这是默认的模式).在这个模式中 ...
- 【JAVA】JDK -Calendar 遇到的 一个坑
Calendar是JDK 1.1增加的类 最近使用了下Calendar发现几个很让人抓狂的问题 源码: public final static int SUNDAY = 1; public final ...