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

来源:互联网 发布:硅谷办公室装修 知乎 编辑:程序博客网 时间:2024/06/08 18:40

Espresso

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

@Testpublic 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 22    buildToolsVersion "22"    defaultConfig {        applicationId "com.my.awesome.app"        minSdkVersion 10        targetSdkVersion 22.0.1        versionCode 1        versionName "1.0"        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"    }}dependencies {    // App's dependencies, including test    compile 'com.android.support:support-annotations:22.2.0'    // Testing-only dependencies    androidTestCompile '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)@LargeTestpublic class HelloWorldEspressoTest {    @Rule    public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule(MainActivity.class);    @Test    public 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

0 0
原创粉丝点击