Espresso

像下面一样写简洁、优美、可靠的Android UI测试:

@Test
public void greeterSaysHello() {onView(withId(R.id.name_field)).perform(typeText("Steve"));onView(withId(R.id.greet_button)).perform(click());onView(withText("Hello Steve!")).check(matches(isDisplayed()));
}

Espresso的核心API很少而且很容易学习,并且由于开源可以自定义。Espresso测试的状态预期、交互、断言很明显是没有破碎的模板内容、自定义结构或者混乱的实现细节。

Espresso测试理想情况下运行很快!当操作UI或者对UI进行断言,界面空闲时允许你在后台进行等待、同步、休眠等操作。

目标受众

Espresso面向认为测试是开发周期中不可或缺的一部分的开发者。由于可以进行黑盒测试,对于了解测试基础代码的人能够解锁Espresso的全部功能。

---espresso-core:包括核心和基础的View匹配器、动作、断言。详见Basics和 Advanced Samples。

---espresso-web:包含支持WebView的资源。

---espresso-idling-resource:Espresso同步后台工作的机制。

---espresso-contrib:外部支持(External contributions)包含日期选择器、 RecyclerView和绘制动作、断言检查、CountingIdlingResource。

---espresso-intents:hermetic测试的有效扩展名和stub意图。

最新的版本和发布信息可以在 Downloads中查看。

Espresso 安装指南

本指南包含通过SDK管理器安装Espresso和通过Gradle来进行创建。建议用Studio。

设置测试环境

为列避免意想不到的情况,我们强烈建议你关闭虚拟或物理测试设备上的系统动画。

---你设备上,在设置--> 开发者选项 中禁止下面3项设置:

---窗口动画缩放(Window animation scale)

---过渡动画缩放(Transition animation scale)

---动画程序时长缩放(Animator duration scale)

下载Espresso

---确保在Extras下安装了最新的Android Supprot Repository(详见instructions)。

---打开app下的build.gradle文件。这个通常不是顶层的build.gradle文件而是app层的build.gradle。

---在依赖中添加下面的代码:

androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'
androidTestCompile 'com.android.support.test:runner:0.5'

---更多请参考 downloads部分(espresso-contrib, espresso-web,等等)

设置工具运行器

在app的build.gradle的android.defaultConfig下添加如下代码:

testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

build.gradle文件的样例

apply plugin: 'com.android.application'android {compileSdkVersion 22buildToolsVersion "22"defaultConfig {applicationId "com.my.awesome.app"minSdkVersion 10targetSdkVersion 22.0.1versionCode 1versionName "1.0"testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"}
}dependencies {// App's dependencies, including testcompile 'com.android.support:support-annotations:22.2.0'// Testing-only dependenciesandroidTestCompile 'com.android.support.test:runner:0.5'androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'
}

分析数据

为了保证我们所追踪到每个最新产品,测试的运行器收集分析数据。具体就是,在启动每个测试之前将应用的包名的哈希值上报。这样可以测量唯一的包名使用Espresso的次数和使用量。

如果你不想上报这个数据,你可以通过下面的测试运行器参数disableAnalytics "true"来禁止上报(详见 how to pass custom arguments)。

创建第一个测试文件

Android Studio默认在src/androidTest/java/com.example.package/下创建了测试文件。

JUnit4中的测试规则样例:

@RunWith(AndroidJUnit4.class)
@LargeTest
public class HelloWorldEspressoTest {@Rulepublic ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule(MainActivity.class);@Testpublic void listGoesOverTheFold() {onView(withText("Hello world!")).check(matches(isDisplayed()));}
}

注:上面这段测试代码需在代码中创建一个MainActivity,布局中有一个Textview,并且设置了内容“Hello world!”,测试的目的是看MainActivity布局中显示为“Hello World”的TextView是否显示

在Android Studio中运行测试文件

创建测试配置

在Android Studio中:

--- 打开   Run  --> Edit Configurations

--- 添加一个新的 Android Tests configuration

---选择一个项目

---添加指定的运行器:

android.support.test.runner.AndroidJUnitRunner

运行刚创建的配置文件。

通过Gradle的命令行运行

执行

./gradlew connectedAndroidTest

Espresso基础

Espresso的API鼓励开发者明确地思考用户与应用交互时可能的操作--定位一个UI并且进行交互。同时,该框架禁止直接访问应用中的Activity和View,因为持有该对象并且在非UI线程中进行操作是引起意想不到的情况的主要原因。因此,你将不会在Espresso API中看到像getView和getCurrentActivity这样的方法。你仍可以通过实现你资源的ViewActions和ViewAssertions来安全地对view进行操作。

如下是Espresso种各主要部分的预览:

---Espresso:与view交互的一个入口(通过onView和onData)。也暴露了不需要与任何view相关联的API(例如pressBack).

---ViewMatchers :实现了Matcher<? super View>接口的一系列对象的集合。你也可以向onView方法中传入一个或者多个来定位当前view层级中的view

---ViewActions :可以作为参数传入ViewInteraction.perform()方法(如click())的ViewAction的集合。

---ViewAssertions:可以作为参数传入方法ViewInteraction.check()的ViewAssertions的集合。

示例:

onView(withId(R.id.my_view))      // withId(R.id.my_view) is a ViewMatcher.perform(click())               // click() is a ViewAction.check(matches(isDisplayed())); // matches(isDisplayed()) is a ViewAssertion

通过onView查找一个view

在大多数例子中,传入hamcrest匹配器的onView方法用来匹配当前view层级中的一个view。对于熟悉如何在Mockito或Junit中使用Matchers的人Mathers将会很强大。如果你对hamcrest匹配器不熟悉,我们建议你从 this presentation(http://www.slideshare.net/shaiyallin/hamcrest-matchers)开始学习。

通常所描述的view都有一个唯一的R.id,一个简单的withId匹配器将会缩小查找的范围。然而,在很多情况下在开发测试时你并不能确定R.id。例如,指定的view也许没有R.id或者R.id并不唯一。普通的测试指南这时不适用并且很难进行代码编写因为通常的方法(findViewById())来访问view并不起作用。因此,你也许需要访问持有该view的Activity或者Fragment或者查找一个已知id的容器,进而导航到当前这个特殊的view。

Espresso通过使用已存在的ViewMatcher或者你自定义的来缩小查找的view范围。

用如下的例子来进行view的查找:

onView(withId(R.id.my_view))

有时,R.id的值在多个view间共用。这种情况下使用R.id将会抛出一个AmbiguousViewMatcherException(例如)异常。异常信息提供了一个当前的view层级中代表该view的文本信息,通过这个信息可以查找到与这个不唯一的R.id匹配的所有view。

java.lang.RuntimeException:
com.google.android.apps.common.testing.ui.espresso.AmbiguousViewMatcherException:
This matcher matches multiple views in the hierarchy: (withId: is <123456789>)

...

+----->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****

浏览view的所有属性,你可以找到唯一识别的属性(在上面的例子中,一个view有text为“Hello!”)。你可以通过结合匹配器来缩小搜索的范围:

onView(allOf(withId(R.id.my_view), withText("Hello!")))

你也可以通过not来反转任何匹配:

onView(allOf(withId(R.id.my_view), not(withText("Unwanted"))))

查看   ViewMatchers  来看Espresso所提供的匹配器。

注:在性能良好的应用中,所有用户可以进行交互的view需要包含描述性的文本或者有内容描述(详情请查看 Android accessibility guidelines。)如果你通过“withText”或者"withContentDescription"进行onView查找如果不能缩小查找的范围,可以把它当成一个可以访问的bug。

注:用最少的描述匹配器来找到你所查询的view。不要进行过多的描述因为这会强制框架去做更多不必要的工作。例如,如果一个view在文本描述上是唯一的,你不需要再通过TextView的赋值来查找。对于大多数view来说R.id应该就足够了。

注:如果目标view存在于AdapterView(例如ListView,GridView,Spinner)中,onView方法可能会不管用,推荐使用onData方法。

为view执行一个动作

当你为目标view找到一个合适的匹配器时,可以通过perform方法来为这个view执行ViewAction。

例如,点击一个view:

onView(...).perform(click());

你也可以在一次调用中同时执行多个动作:

onView(...).perform(typeText("Hello"), click());

如果你要操作的view位于ScrollView内(纵向或横向),需要考虑到与scrollTo()连用时(如click()和typeText())需要view进行显示。这样保证了在进行其它操作之前该view已经显示。

onView(...).perform(scrollTo(), click());

注:如果view已经显示,那么scrollTo()将不会起作用,在某些情况下如在大分辨率下显示该view时可以安全地使用该方法。(例如,你同时在小屏幕和大屏幕分辨率的手机上同时进行测试)

看 ViewActions查看Espresso更多的view动作

检查view是否与断言相符

通过方法check()断言可作用于当前选中的view。使用最多的是matches()断言,它通过ViewMatcher来断言当前选中的view的状态。

例如,检测一个view是否设置文本“Hello!”:

onView(...).check(matches(withText("Hello!")));

注:方法onView中不要包含参数“assertions”--相反,在check块中直接声明你所要检测的内容。例如:

如果你想要断言一个view中设置了"Hello!",下面是一个不好的示例:

// 不要在onView内使用像withText之类的断言
onView(allOf(withId(...), withText("Hello!"))).check(matches(isDisplayed()));

另一方面,如果你想要断言一个设置“Hello!”的view没被隐藏---例如改变了view的可见不可见的标记---上面的代码是可以的

注:断言一个view没有显示与断言一个view不存在于当前的view层级是不同的


使用onView的小例子

在这个例子中SimpleActivity包含一个Button和一个TextView。当点击button后TextView的内容改为"Hello Espresso!".下面演示如何通过Espresso来进行检测:

1.点击按钮

第一步就是通过合适的属性找到button。在SimpleActivity中button有一个唯一的R.id----完美!

onView(withId(R.id.button_simple))

现在只想点击

onView(withId(R.id.button_simple)).perform(click());

2.检测TextView设置了文本"Hello Espresso!"

设置了文本的TextView证实也有一个唯一的R.id:

onView(withId(R.id.text_simple))

现在检测文本

onView(withId(R.id.text_simple)).check(matches(withText("Hello Espresso!")));

AdapterView中使用onData(ListView,GridView....)

AdapterView是一种特殊的类型,它从Adapter中动态地加载数据。AdapterView最通常的例子就是ListView。与LinerLayout静态widgets相反,只有一部分AdapterView子view会被加载到当前的view层级中,通过onView进行查找时当前未加载的view将不会被找到。Espresso通过onData()预先操作它或它的子类来处理即将加载的adapter的item。

注:初始化时就显示在屏幕上的adapter中的view你也可以不适用onData()因为他们已经被加载了。然而,使用onDta()会更加安全。

提醒:在打破了继承约束(尤其是getItem()的API)实现了AdatpterView的自定义view中onData()是有问题的。在这中情况下,做好的做法就是重构应用的代码。如果不重构代码,你也可以实现自定义的AdapterViewProtocol来实现。查看Espresso的AdapterViewProtocols 来查看更多信息。

onData的测试样例

这个例子演示了如何使用onData()。在SimpleActivity中包含几个条目信息的Spinner---文字代表咖啡饮料的类型。当选中一个条目时,TextView将显示成"One %s a day!",%s是选中的条目。目标是检测打开一个Spinner,选中一个条目,然后确定TextView中包含那个条目。由于Spinner是AdapterView的子类,建议使用onData()而不是onView()来进行条目的匹配。

1.点击Spinner来打开要选择的条目

onView(withId(R.id.spinner_simple)).perform(click());

2.点击条目“Americanno”

Spinner将内容以ListView的形式展开---内容可能会非常长,有的不在当前的view层级---通过使用onData()我们强制所需要的view元素加载到当前的view层级。Spinner中的内容类型是String,我们想匹配一个条目是String类型,文字是“Americano”:

onData(allOf(is(instanceOf(String.class)), is("Americano"))).perform(click());

3.检测TextView包含String “Americano”

onView(withId(R.id.spinnertext_simple)).check(matches(withText(containsString("Americano"))));
测试

当测试失败时Espresso提供了有用的debugging信息

日志

Espresso打印了所有view的动作信息。例如:

ViewInteraction: Performing 'single click' action on view with text: Espresso

View层级

当onView()执行失败时Espresso打印view层级的异常信息。当onView()找不到目标view时,将会抛出NoMatchingViewException异常。你可以通过查看view层级的异常信息来查看匹配器为什么不能匹配任何view。如果OnView()查找到了与匹配器匹配的多个view,AmbiguousViewMatcherException异常将会抛出。view层级信息将会打印并且所有匹配的view将会标上MATCHES标签:

java.lang.RuntimeException:
com.google.android.apps.common.testing.ui.espresso.AmbiguousViewMatcherException:
This matcher matches multiple views in the hierarchy: (withId: is <123456789>)

+----->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****

当处理复杂的view层级或者意料之外的控件时使用Android View Hierarchy Viewer 是有效的。

AdapterView警告

Espresso在出现AdapterView时会提示警告。当onView()查找显示在当前view层级中AdapterView中的view但是却抛出NoMatchingViewException异常时,通常的解决办法就是使用onData().异常信息包含一系列adapter 中view的警告。你可以查看这些信息通过与onData结合来查找目标view。

Espresso 速查表

Espresso速查表在开发时可以快速查询。这个速查表包含大部分Matchers,ViewActions和ViewAssertions.

离线pdf版: espresso-cheat-sheet-2.1.0.pdf

原文地址:https://google.github.io/android-testing-support-library/docs/espresso/index.html

Espresso指南一(Espresso下载、安装、设置、基础、速查表)相关推荐

  1. antlr4权威指南中文pdf_Python 数据科学速查表中文版(全套下载)

    向AI转型的程序员都关注了这个号??? 机器学习AI算法工程   公众号:datayx Python 数据科学速查表中文版(全套下载) 关注微信公众号 datayx  然后回复  数据科学  即可获取 ...

  2. 超全的Matplotlib速查表,打包下载

    众所周知,Matplotlib是Python可视化的基础库,能绘制二维.三维.动态交互式的图表,而且可以作为图像处理工具,制作艺术风格的可视化大图. Matplotlib还是众多可视化库的底层依赖,比 ...

  3. 资源|最好的九张机器学习/深度学习代码速查表,附高清下载

    作者:Kailash Ahirwar 机器之心编译 文末附高清速查表下载 对于初学者来讲,入门机器学习和深度学习非常困难:同时深度学习库也难以理解.通过收集多方资源,我在 Github 上创建了一个速 ...

  4. Python常用网络爬虫速查表下载

    Python常用网络爬虫速查表下载 Post方法: Get方法: css选择器 beautiful soup选择器 xpath选择器 可以将图片打印出来,放在桌面看 下载地址: 一天掌握python网 ...

  5. 下载 | 《javascript速查表中文版》

    今天给大家分享老曾制作的js cheat sheet,直接看图⬇️ 小抄资料获取 关注左侧[web前端营] 回复 20009 javascript 简称js 前端编程(薪资蛮高的)编程语言 哦,就是做 ...

  6. 纯新手Oculus Quest2 安装sidequest 入门教学指南,支持下载安装第三方app

    下载安装sidequest 1.https://github.com/the-expanse/SideQuest 在github下载最新release的版本. 2.下拉到Assets,根据电脑系统选择 ...

  7. pycharm安装教程(下载安装+设置中文界面)

    pycharm是python编辑器之一,用来编辑python代码及调试.可以跨平台,在macos和windows下面都可以很方便的使用. 下面以笔者在用的2022专业版为例,详细演示pycharm安装 ...

  8. mantis下载安装设置

    下载XAMPP(https://www.apachefriends.org/index.html) 我以前下载的最新版 Apache/2.4.41 (Win64) OpenSSL/1.1.1c PHP ...

  9. fedora安装 设置基础软件仓库时出错

    首先检查你的网络 然后点击安装源,然后配上阿里云的 http://mirrors.aliyun.com/centos/7/os/x86_64/ 我这里时选择的这个版本,根据自己的需求来 然后点击安装

最新文章

  1. DllMain中不当操作导致死锁问题的分析--进程对DllMain函数的调用规律的研究和分析
  2. 不可错过! CMU《高级自然语言处理》
  3. 第六讲:软考中高项06_质量管理、人力资源管理
  4. jenkins查询mysql_jenkins流水线使用mysql数据库
  5. 学什么就业前景好的专业_新能源汽车专业学什么?其就业前景如何?
  6. 图像纹理合成_EnhanceNet:通过自动纹理合成实现单图像超分辨率
  7. esp8266原理图_ESP32/ESP8266使用MicroPython控制DHT11/DHT22
  8. vue 前端显示图片加token_前端甩锅神器:vue中的mock使用
  9. java项目整合mybatis_JavaWeb项目整合Spring,SpringMVC,Mybatis框架
  10. 基于BOW模型的图像检索
  11. 小白版教您如何使用SOLIDWORKS MBD
  12. STM32——PWM知识详解
  13. IT十年人生过客-七-眉毛与恶名
  14. 炉石传说 android,炉石传说安卓版
  15. 千呼万唤始出来 Google GDrive将于4月初正式推出
  16. python0309
  17. Android流式布局FlowLayout,一款针对Tag的布局
  18. Atitit 算法之道 attilax著 1. 编码算法 3 1.1. Base64 htmlencode urlencode 3 2. Ui方面的算法 3 2.1. 软键盘算法 计算软键盘上下
  19. 实验吧-隐写-欢迎来到地狱(Word文档文字隐藏、Rabbit加密)
  20. 力扣1217. 玩筹码

热门文章

  1. 拼多多迈向后黄峥时代
  2. java读取ppm图片_C++ 输出PPM格式图片文件
  3. js 秒转换成时分秒
  4. php imp,Imp_在线英语听力室
  5. C/C++程序的内存开辟
  6. 什么是长尾关键词?长尾关键词优化方法和技巧
  7. godaddy mysql不存在_Godaddy免费空间问题及解决方案总结
  8. c语言程序设计慕课版答案潘晟旻,附件1团队及组成.doc
  9. 【TED ON FLEX】支持flashplayer的RIBBIT系统
  10. TabLayout 不显示下划线