◆版权声明:本文出自胖喵~的博客,转载必须注明出处。

  转载请注明出处: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详解的更多相关文章

  1. “全栈2019”Java多线程第十九章:死锁详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  2. “全栈2019”Java异常第十九章:RuntimeException详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java异 ...

  3. “全栈2019”Java第二十九章:数组详解(中篇)

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  4. Android Developer -- Bluetooth篇 开发实例之四 API详解

    http://www.open-open.com/lib/view/open1390879771695.html 这篇文章将会详细解析BluetoothAdapter的详细api, 包括隐藏方法, 每 ...

  5. Android 音视频开发(六): MediaCodec API 详解

    在学习了Android 音视频的基本的相关知识,并整理了相关的API之后,我们应该对基本的音视频有一定的轮廓了. 下面开始接触一个Android音视频中相当重要的一个API: MediaCodec.通 ...

  6. 第三十九课:requestAnimationFrame详解

    大家应该都知道,如果一个页面运行的定时器很多,无论你怎么优化,最后肯定会超过指定时间才能完成动画.定时器越多,延时越严重. 为此,YUI,kissy等采用中央队列的方式,将定时器减少至一个.浏览器厂商 ...

  7. jQuery 源码分析(十九) DOM遍历模块详解

    jQuery的DOM遍历模块对DOM模型的原生属性parentNode.childNodes.firstChild.lastChild.previousSibling.nextSibling进行了封装 ...

  8. 大白话5分钟带你走进人工智能-第二十九节集成学习之随机森林随机方式 ,out of bag data及代码(2)

              大白话5分钟带你走进人工智能-第二十九节集成学习之随机森林随机方式 ,out  of  bag  data及代码(2) 上一节中我们讲解了随机森林的基本概念,本节的话我们讲解随机森 ...

  9. 第三百二十九节,web爬虫讲解2—urllib库爬虫—ip代理—用户代理和ip代理结合应用

    第三百二十九节,web爬虫讲解2—urllib库爬虫—ip代理 使用IP代理 ProxyHandler()格式化IP,第一个参数,请求目标可能是http或者https,对应设置build_opener ...

随机推荐

  1. LeetCode 372

    题目: Your task is to calculate a^b mod 1337 where a is a positive integer and b is an extremely large ...

  2. Linux C编程学习之开发工具1---GCC编译器

    GCC简介 GCC(GNU Compiler Collection)是一套功能强大.性能优越的编程语言编译器,它是GNU计划的代表作品之一.GCC以GPL和LGPL许可证发行,它是类Unix和苹果电脑 ...

  3. 基于GMap.Net的地图解决方案

    一 地图的加载与显示 关于GMap的介绍与使用可以看我以前的文章: GMap.Net开发之在WinForm和WPF中使用GMap.Net地图插件 GMap.Net是.Net下一个地图控件,可以基于Ht ...

  4. 【leetcode】Remove Element

    题目概述: Given an array and a value, remove all instances of that value in place and return the new len ...

  5. 【Mybatis高级映射】一对一映射、一对多映射、多对多映射

    前言 当我们学习heribnate的时候,也就是SSH框架的网上商城的时候,我们就学习过它对应的高级映射,一对一映射,一对多映射,多对多映射.对于SSM的Mybatis来说,肯定也是差不多的.既然开了 ...

  6. MIT 6.828 JOS学习笔记4. Lab 1 Part 2.1: The Boot Loader

    Part 2: The Boot Loader 对于PC来说,软盘,硬盘都可以被划分为一个个大小为512字节的区域,叫做扇区.一个扇区是一次磁盘操作的最小粒度.每一次读取或者写入操作都必须是一个或多个 ...

  7. 各种解析漏洞获取Webshell

    各种解析漏洞拿shell  一.IIS 6.0解析漏洞 IIS 6.0解析利用方法有两种1.目录解析/xx.asp/xx.jpg2.文件解析wooyun.asp;.jpg第一种,在网站下建立文件夹的名 ...

  8. 分享Kali Linux 2016.2第50周镜像文件

    分享Kali Linux 2016.2第50周镜像文件Kali Linux官方于12月11日发布Kali Linux 2016.2的第50周镜像.这次保持以往规律,仍然是11个镜像文件.默认的Gnom ...

  9. [自动运维]oracle自动备份

    数据是应用的核心部分,程序坏了换台机器重新发布就可以,但数据一旦丢失,造成的损失将不可挽回,程序发布到生产后,数据的备份便显得尤为重要,由于不一定所有的服务均有资金完成高级的备份如RAC和DG,在我们 ...

  10. caffe中权值初始化方法

    首先说明:在caffe/include/caffe中的 filer.hpp文件中有它的源文件,如果想看,可以看看哦,反正我是不想看,代码细节吧,现在不想知道太多,有个宏观的idea就可以啦,如果想看代 ...