[toc]
上一篇说道如何在AndroidStudio中配置Espresso,本篇主要介绍使用Espresso时的思考和示例(示例已托管在github);
关于Espresso的一些杂谈
愿景
愿你沉醉在单元测试中日渐消瘦,无法自拔~
愿你归来时,仍是少年。
对Espresso的基本印象
AndroidJUnitRunner:适用于 Android 且与 JUnit 4 兼容的测试运行器
Espresso:UI 测试框架;适合应用中的功能性 UI 测试
UI Automator:UI 测试框架;适合跨系统和已安装应用的跨应用功能性 UI 测试
Espresso 测试框架提供了一组 API 来构建 UI 测试,用于测试应用中的用户流。利用这些 API,可以编写简洁、运行可靠的自动化 UI 测试。
Espresso 测试框架的主要功能
视图匹配
操作 API
UI 线程同步
系好安全带,开车了
单一特性查找控件
现在有这么一个情况,代码刚写完,迫不及待的想看一下里边的控件(视图)表现是否得体。
怎么能设计一个套路,来检测一下?
首先,得找到你想找的控件(视图)。
那么问题来了,茫茫人海,一叶扁舟,怎么才能找到想要的控件(视图)?
俗话说:“*****,捉贼拿脏”,先找一下控件的特征:)
●onView(withId(R.id. jack)) 【在知道控件id的情况下】
●onView(withText("Jack"))【有文本"Jack"这几个字的文本控件】
●onView(withTagKey("full_name"));【根据setTag(key,value)来查找】
●onView(withTagValue("Jack_Dawson"));【Jack的全名叫杰克.道森,tag的值】
●onView(withHint("提示词")); 【忘词了,居然有提示词 eg.EditText】
….
and so on 可以根据指定的特征来对控件进行筛选查找【文末的附图有详细的介绍】
多个特性查找控件
但是,有时候我们会遇到这种情况:
多个页面中控件的id是一样的,这个时候就要需要根据多个特性来筛选了。
毫无疑问,一个惊天地、泣鬼神的API就要出世了。不错,它就是智慧与美貌并存,正义与侠义的化身的 allof()
在Matchers类下边有一个方法allOf(),参数是一个可变参数,我的个乖乖,这样以上的单一的特性就可以,添加多特性筛选了;
具体示例如下:
onView(allOf(withId(R.id.jack),withText("Jack")));[注:可变参数可以传递多个参数] 一个ID叫jack,并且显示了“Jack”文字的控件,当然可以加很多限制条件,只有你有都可以传递在allOf的可变参数中 |
至此,在Espresso中查找一个控件不在是难事了:)
东风已借,完事具备,开测了、开测了!
show me the code!
测RecyclerView引发的血案
public class RecyclerViewTest extends ActivityInstrumentationTestCase2<RecyclerViewActivity> { private static final String BOOK_TITLE = "Clean Code"; private static final String BOOK_AUTHOR = "Robert C. Martin"; public RecyclerViewTest() { super(RecyclerViewActivity.class); } @Override protected void setUp() throws Exception { super.setUp(); getActivity();//设置运行上下文 } //RecyclerViewActions为Espresso的提供API /** * 根据Item的position来进行测试 */ public void testClickAtPosition() { onView(withId(R.id.recyclerView)).perform(RecyclerViewActions.actionOnItemAtPosition(0, click()));//点击RecyclerView的第一个条目 onView(withId(R.id.book_title)).check(matches(withText(BOOK_TITLE)));//检查控件文本内容是否和BOOK_TITLE内容一致 onView(withId(R.id.book_author)).check(matches(withText(BOOK_AUTHOR)));//同上 } public void testClickOnViewInRow() { //点击一个有具体描述的文本 onView(withId(R.id.recyclerView)).perform(RecyclerViewActions.actionOnItem( hasDescendant(withText(BOOK_TITLE)), click()));// onView(withId(R.id.book_title)).check(matches(withText(BOOK_TITLE))); } } |
附:
1.RecyclerViewActivity public class RecyclerViewActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_recycler_view); RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerView); recyclerView.setLayoutManager(new LinearLayoutManager(this)); recyclerView.setAdapter(new RecyclerBooksAdapter(this)); } } 2.item_book.xml <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="12dp"> <ImageView android:id="@+id/icon" android:src="@drawable/book" android:layout_width="40dp" android:layout_height="40dp" android:layout_marginRight="8dp" /> <TextView android:id="@+id/book_title" android:layout_toRightOf="@id/icon" android:layout_toEndOf="@id/icon" android:layout_width="fill_parent" android:layout_height="wrap_content" android:textSize="16sp" tools:text="Some Cool Book" /> <TextView android:id="@+id/book_author" android:layout_below="@id/book_title" android:layout_toRightOf="@id/icon" android:layout_toEndOf="@id/icon" android:layout_width="fill_parent" android:layout_height="wrap_content" android:textSize="12sp" tools:text="by Awesome Author" /> </RelativeLayout> |
由一句话引发的思考
onView(withId(R.id.recyclerView)).perform(RecyclerViewActions.actionOnItemAtPosition(0, click()));
关于perform()函数的二三事:
一眼就能看出,能使用perform函数的都是演员;演员是那些控件(View),现在,编剧是你,导演是你,看你想这个演员(控件|View)怎么演了。
perform的传参:可变参数,对,你没看错,还是可变参数。这就意味着,你可以让演员演一系列的动作(Action|ViewAction)。然鹅,还是要对表演的套路做一些了解的,且看一下ViewAction的基本套路。
ViewAction中都有哪些动作呢?
clearText()清空文本
click()点击动作
swipe系列 [猛击,这个厉害了] swipeLeft() 原版解释:swipe right-to-left across the vertical center of the view swipeRight() 原版解释:swipe left-to-right across the vertical center of the view swipeDown() 原版解释: top-to-bottom swipeUp() 原版解释: bottom-to-top |
closeSoftKeyboard() 这个不用多说了吧,关闭软键盘
pressBack() 相当于点击了返回键了
pressKey(keyCode) 还可以根据keycode来点击按键
doubleClick() 双击
longClick()
等一系列操作,限于篇幅就不在过多的展开了,想看详细的可以直接去ViewAction类下边去看,只有你想不到的没有你看不到的~
后话:其他的Action多多少少和ViewAction有一些说不清道不明的关系,相当之暧昧,比如:RecyclerViewActions、EditorAction、CloseKeyboardAction、ScrollToAction等,有兴趣的可以看一下它们的实现
check()函数的二三事
演员演完了,这时候作为导演就得看一下演员演的是否到位,关键是否达到了预期的效果,如果你要加点潜规则啥的都看你自己了:)
来定义一点规则吧,不然演员就瞎演了。
●check函数的传参ViewAssertion(Checks the state of the given view (if such a view is present).)【大佬们的命名真腻害,这不就是对这个控件的断言吗?】
那么问题来了,check到底能检查一些什么信息?且听我细细道来。
先举个简单的例子吧:
onView(withId(R.id.jack)).check(matches(isDisplayed()));//检查控件是否显示了 onView(withId(R.id.jack)).check(matches(not(isDisplayed()));//not onView(withId(R.id.jack)).check(matches(withHint("Jack")));//检查提示词是否正确 onView(withId(R.id.jack)).check(matches(withText("Jack")));//检查文本 |
当然还有很多可以检测的类型,例如isEnabled()、isFocusable()、hasFocus()等等
所有的可检测的类型都在ViewMatchers类里边了,想了解的少年可以去瞅瞅
【附】
●示例传送门
https://github.com/ZoranLi/EspressoExample github地址
●附图
image.png
相关文章链接: