前言
Espresso API
鼓励测试作者根据用户在与应用程序交互时可能做什么 - 定位UI元素并与其交互。同时,框架阻止对应用程序的活动和视图的直接访问,因为保持这些对象并在UI线程上对它们进行操作是测试flakiness的主要来源。因此,您不会在Espresso API中看到类似getView和getCurrentActivity的方法。您仍然可以安全地实现自己的ViewAction和ViewAssertion。
以下是Espresso的主要组件概述:
Espresso -入口点的交互通过onView和onData)。
ViewActions -集合ViewAction中的可以传递给ViewInteraction.perform()方法(例如,click())。可以执行模拟事件。
ViewAssertions -对象ViewAssertion中的可传递的ViewInteraction.check()方法。大多数时候,使用matches断言来进行对比是否符合预期,它使用View匹配器来断言当前选择的视图的状态。(是否显示在界面,是否可点击,是否符合预期等)
ViewMatchers -实现对象的集合Matcher接口。可以通过一个或多个Matcher规则来定位当前视图层级中的图。
上面三个组件构成了Espresso的整个体系
Example:
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查找视图
在绝大多数情况下,onView方法使用一个hamcrest匹配器框架,该匹配器匹配当前视图层次结构中的一个(且只有一个)视图。Matchers是强大的,与Mockito或Junit 的匹配规则很相似。如果你不熟悉hamcrest的匹配,我们建议您先快速浏览一下
Blog这个演示:http://www.slideshare.net/shaiyallin/hamcrest-matchers 或者查看测试脚本android_testing
通常情况下,所需匹配的视图具有独特的R.id和简单的withId匹配将缩小视图搜索。不过,也有当你无法判断许多合法的情况下,R.id在测试开发时间。例如,特定视图可能不具有R.id或R.id不是唯一的。这可以使 instrumentation tests 来进行测试,因为findViewById()的方式来访问预览是不工作的。因此,您可能需要访问持有视图的Activity或Fragment的私有成员,或者找到具有已知R.id的容器,并导航到特定视图的内容。
Espresso 允许你使用ViewMatchers缩小或者使用其它方式(自定义视图匹配器)进行缩小视图层次范围,达到最终定位视图层次中的视图。
//通过其R.id查找视图非常简单:
onView(withId(R.id.my_view))
有时,在多个视图之间共享R.id值。当发生这种情况的尝试以使用特定的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 <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****
通过查看视图的各种属性,您可能会发现唯一可识别的属性(在上面的示例中,其中一个视图具有文本“Hello!”)。您可以使用此方法通过使用组合匹配器缩小搜索范围:
onView(allOf(withId(R.id.my_view), withText("Hello!"))) allof(指定两者都符合)
您还可以使用not扭转任何匹配器:
onView(allOf(withId(R.id.my_view), not(withText("Unwanted"))))
见ViewMatchers由Espresso提供的视图的匹配。
注意:在运行良好的应用程序,所有视图,用户都应包含说明性文字或有内容描述(见Android的易用性原则如果您在使用OnView 的“withText’或’ withContentDescription’ 无法缩小搜索。考虑将它作为一个可访问性的bug。
注意:用最少的描述匹配去寻找。不要过度指定匹配条件,因为这将迫使框架做比必要的更多的工作。例如,如果一个视图是由它的文本唯一标识,则不需要指定R.id.myid 来进行匹配,WithText( “ 文本唯一”),可以完成大部分的匹配
注意:如果目标视图在AdapterView(例如ListView,GridView,Spinner)的内部,onView方法可能无法正常工作,并建议使用onData方法来代替。
对视图执行操作
当找到了目标视图的适当匹配,则能够进行ViewAction利用上perform的方法。
例如,点击视图:
onView(...).perform(click());
您可以使用一个执行调用执行多个操作:
onView(...).perform(typeText("Hello"), click());
如果你正在使用的视图位于内ScrollView(垂直或水平),考虑上述要求需要提前进行显示(如视图操作click()和typeText())用scrollTo()。这确保在继续执行其他操作之前显示视图:
onView(...).perform(scrollTo(), click());
注:,如果已经显示视图, scrollTo()不会有任务动作,
见ViewActions由Espresso所提供的视图操作。
检查视图是否满足断言
断言可应用于与当前选择的视图check()的方法。最常用的断言是matches()断言,它使用一个ViewMatcher断言当前选择的视图的状态。
例如,要检查视图是否包含文本“Hello!”:
onView(...).check(matches(withText("Hello!")));
注意:不要把“assertions” 作为OnView的参数,相反的,应该明确在校验块内检查。例如:
如果你想断言“Hello!”是视图的内容,以下被认为是不好的做法:
// Don't use assertions like withText inside onView.onView(allOf(withId(...), withText("Hello!"))).check(matches(isDisplayed()));
另一方面,如果要断言具有文本“Hello!”的视图是可见的 - 例如在视图可见性标志更改之后 - 代码是正常的。
注意:一定要注意相互之间的差别声称视图不显示和不存在。
开始使用onView进行简单测试
在本例中,SimpleActivity中含有一个Button与一个TextView。当按钮被点击的内容TextView变化”Hello Espresso!”。以下是如何用Espresso测试这个:
1.单击按钮
第一步是寻找有助于匹配按钮的属性。在该按钮SimpleActivity具有独特唯一属性的是
R.id.button_simple!
onView(withId(R.id.button_simple))
现在执行点击:
onView(withId(R.id.button_simple)).perform(click());
2.检查TextView现在包含“Hello Espresso!”
onView(withId(R.id.text_simple))
现在验证内容文本:
onView(withId(R.id.text_simple)).check(matches(withText("Hello Espresso!")));
使用ondata()来定位AdapterView(ListView,GridView…)
AdapterView 视图是一种适配器动态加载其数据的特殊类型的小窗口,一个最常见的例子就是ListView。内容是动态加载的,而onView()可以处理的是已经显示在屏幕上的组件。Espresso提供一个单独的处理,使用onData()作为切入点,它能够在AdapterView 子类工作之前,优先加载,进行匹配。
警告:
自定义实现AdapterView的View使用onData()方法进行匹配是有问题的,如果更改了getitem()方法。(ondata()会使用到getItem()方法进行匹配),在这种情况下,最好的做法是重构应用程序代码。如果你不能做到这一点,你可以实现一个匹配的定制 AdapterViewProtocol。
开始使用onData进行简单测试
这个简单的测试演示了如何使用onData()。这个测试的目的是打开Spinner,选择一个特定的item,然后验证TextView包含在item中的子类。作为Spinner类是基于AdapterView它建议使用onData(),而不是onView()用于匹配的项目。
1.单击Spinner 打开选择列表
onView(withId(R.id.spinner_simple)).perform(click());
2.单击item“Americano”
对于使用listview来加载视图,有可能会有多于一屏的情况,有些元素是看不到的,通过使用onData()我们强迫我们想要的元素显示到当前视图层次。我们要匹配是一个字符串,它等于字符串“Americano”的item:
onData(allOf(is(instanceOf(String.class)), is("Americano"))).perform(click());
3.验证TextView包含字符串“美式”
onView(withId(R.id.spinnertext_simple))
.check(matches(withText(containsString("Americano"))));
调试
当测试失败时,Espresso提供有用的调试信息:
记录
Espresso将所有视图操作记录到logcat。例如:
ViewInteraction: Performing 'single click' action on view with text: Espresso
如果onView()未找到目标来看,一个NoMatchingViewException被抛出。您可以检查异常字符串中的视图层次结构,以分析匹配器为什么不匹配任何视图。*如果onView()找到匹配给定匹配的多个视图,一个AmbiguousViewMatcherException被抛出。将打印视图层次结构,并且匹配的所有视图都标记为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****
AdapterView 警告
Espresso发出警告,用户进行匹配AdapterView子类。使用onView()操作会引发NoMatchingViewException和AmbiguousViewMatcherException:,最常见的解决方案是使用onData()。可以根据异常信息进行bug修复
引用:
官方文档:https://google.github.io/android-testing-support-library/docs/espresso/basics/index.html