ps:Espresso英文文档,本人翻译水平有限,可能存在不足
Espresso是Google官方提供的Android UI自动化测试的框架。
使用Espresso能写出简洁,美观,可靠的android ui test(好处)
Espresso的重要组成部分:
1.Espresso:通过onView()和onData()与view交互的进入点,它的api不依赖任何view
2.ViewMatchers: 实现了Matcher<? super View>的集合对象。通过onView()来定位当前的view
3.ViewActions: 具备操作方法(例如点击操作)的集合对象,它里面的操作可以通过
ViewInteraction.perform()来实现
4.ViewAssertions 用它可以断言,查看当前view的状态 ,ViewInteraction.check() 会执行它
来张Espresso的小抄:
案例:
onView(withId(R.id.main_view)).perform(click()).check(matches(isDisplayed()));
分析:withId()通过id获取到ViewMatcher,
click()是一个viewAction(即操作),
matches()是一个ViewAssertions
Espresso的使用步骤:
步骤(一):加载view
1.普通的view用onView()加载视图:
1.1:R.id.xx是唯一的: onView(withId(R.id.main_view))
1.2:存在view的id是不唯一:若是通过以上方法会报错,com.google.android.apps.common.testing.ui.espresso.AmbiguousViewMatcherException
解决方式:缩小范围,通过查看所要找到的view属性,采用多个属性来查找
案例:若是两个edittext中的id相同,text内容不同,现在要获取text内容为"Hello!":
//通过id,text属性来确定所要查找的edittextview:
onView(allOf(withId(R.id.my_view), withText("Hello!")))
2.AdapterView类型(例如gridview,listview,spinner)中的itemView:要通过onData()加载视图
特殊情况: 为了解决view中R.id问题(可能不存在,不唯一)的问题,获取view视图可以通过自定义(或者已经存在)的ViewMatchers
步骤(二):执行action
在view中执行一个操作:
在匹配好指定view(即onView()或者onData())后,可以通过perform()来执行ViewAction
案例之view的点击: onView(withId(R.id.main_view)).perform(click())
案例之view执行多个操作: onView(...).perform(typeText("Hello"), click());
案例之ScrollView中的view执行:onView(...).perform(scrollTo(), click());
理解:执行click()随着sroolrto() ,确保执行操作在其他操作之前
步骤(三):检查view是否包含某种assertion
当前的view可以通过check()执行Assertions 。
通常通过matches()使用 assertion,matches()是通过 ViewMatcher来断言当前view的状态
案例1:检查view是否包含“Hello”内容
onView(...).check(matches(withText("Hello!")));
案例2:若是view没有明确displayed的情况下,不能将assertions放到onView()中,要将检查放到检查代码块中:
// view的disPlay是未知情况下,assertions(断言)view的内容是否是“Hello!”,以下代码是不好的
onView(allOf(withId(...), withText("Hello!"))).check(matches(isDisplayed()));
若是已经明确view已经显示,这代码是好的。
注意点:在view不显示或者view不在当前视图中这两种情况下,使用assertions情况
使用IDE开始编写:
(一)eclipse中Espresso的使用:
配置Espresso, 通过添加以下静态jar:
github上包含Android Studio和eclipse的对应项目
(下载全部案例后,使用eclipse的,选择BasicSampleBundled):
eclipse的案例:
https://github.com/googlesamples/android-testing/tree/master/ui/espresso/BasicSampleBundled
(二)Android Studio中Espresso的使用:
第一步:添加 Android Support Repository,(已经添加的,这步省略)
第二步:在Gridle中dependencies{}添加各自需求的jar:
//Android JUnit Runner
androidTestCompile 'com.android.support.test:runner:0.5'
// JUnit4 Rules
androidTestCompile 'com.android.support.test:rules:0.5'
// Espresso core
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'
//测试DatePicker, RecyclerView, Drawer actions,
Accessibility checks, CountingIdlingResource所需
androidTestCompile 'com.android.support.test.espresso:espresso-contrib:2.2.2'
//测试WebView所需
androidTestCompile 'com.android.support.test.espresso:espresso-web:2.2.2'
//测速异步线程所需
androidTestCompile 'com.android.support.test.espresso:espresso-idling-resource:2.2.2'
最后别忘记,sync Now
第三步:创建测试包的路径,切换到project视图下,找到对应的项目,在src下创建一个文件,命名为androidTest/java,然后在anroidTest/java路径下创建一个包,填入项目的包名(已经存在对应项目的android test包,省略这步)
第四步:在xxx.xxx.xx(androidStudio)下创建对应的测试类,按照Espresso使用方式,编写测试代码
第五步:创建test configuration: Edit Configurations–> +–>Android Tests configuration–>选择module和添加AndroidJUnitRunner ,name 自己定义,module 选择要测试的项目
最后一步:运行测试项目
测试案例:Button点击和ListView的item点击测试:
在配置Espresso的jar你会发现一些问题:
Error:Conflict with dependency 'com.android.support:support-annotations'. Resolved versions for app (23.3.0) and test app (23.1.1) differ.
See http://g.co/androidstudio/app-test-app-conflict for details.
这是两个注解包冲突了
解决方式:采用你当前项目中com.android.support:support-annotations(个人这边是23.3.0版)
gradle中jar依赖:
dependencies {
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.3.0'
//添加注解,包含test
compile 'com.android.support:support-annotations:23.3.0'
//解决冲突
androidTestCompile 'com.android.support:support-annotations:23.3.0'
// Android JUnit Runner
androidTestCompile 'com.android.support.test:runner:0.5'
// JUnit4 Rules
androidTestCompile 'com.android.support.test:rules:0.5'
// Espresso core
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'
}
Button所在的xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:orientation="vertical"
tools:context="com.szejq.testdemo.MainActivity">
<TextView
android:id="@+id/main_change"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!" />
<EditText
android:id="@+id/main_edit"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<Button
android:id="@+id/main_btn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="changContent"
android:text="button"/>
<ListView
android:id="@+id/main_list"
android:layout_width="match_parent"
android:layout_height="match_parent"></ListView>
</LinearLayout>
myadapter_item(listView中item)的xml:
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:id="@+id/myadapter_item"
android:layout_height="match_parent">
</TextView>
MainActivity.java的代码:
public class MainActivity extends AppCompatActivity {
private ListView listView;;
private List<String> list;
private TextView showChangge_tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
listView=(ListView) this.findViewById(R.id.main_list);
list=new ArrayList<>();
for (int i=1;i<6;++i){
list.add(String.valueOf(i));
}
listView.setAdapter(new MyAdapter());
showChangge_tv= (TextView) this.findViewById(R.id.main_change);
}
public void changContent(View v){
showToas("onClick");
}
public Toast toast;
public void showToas(String content){
if(toast!=null){
toast.cancel();
}
toast=Toast.makeText(getApplicationContext(),content,Toast.LENGTH_SHORT);
toast.show();
}
private class MyAdapter extends BaseAdapter implements View.OnClickListener{
@Override
public int getCount() {
return list.size();
}
@Override
public Object getItem(int position) {
return list.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
if(convertView==null){
viewHolder=new ViewHolder();
convertView=View.inflate(MainActivity.this,R.layout.myadaper_item,null);
viewHolder.textview=(TextView) convertView.findViewById(R.id.myadapter_item);
convertView.setTag(viewHolder);
}else{
viewHolder=(ViewHolder) convertView.getTag();
}
viewHolder.textview.setText(list.get(position));
viewHolder.textview.setOnClickListener(this);
return convertView;
}
@Override
public void onClick(View v) {
showChangge_tv.setText(((TextView)v).getText());
}
}
static class ViewHolder{
TextView textview;
}
}
MainActivity对应测试类中代码(重要点):
注意点:@xx这些注解不能少,导入jar需通过static import
package com.szejq.testdemo;
import android.support.test.espresso.Espresso;
import android.support.test.espresso.ViewAction;
import android.support.test.espresso.ViewInteraction;
import android.support.test.espresso.action.ViewActions;
import android.support.test.espresso.matcher.ViewMatchers;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.LargeTest;
import android.view.View;
import org.hamcrest.Matcher;
import org.hamcrest.Matchers;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import static android.support.test.espresso.Espresso.onData;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.assertion.ViewAssertions.matches;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static android.support.test.espresso.matcher.ViewMatchers.withText;
/**
* Created by Administrator on 2016/7/19.
*/
@RunWith(AndroidJUnit4.class)
@LargeTest
public class MainActivityTest {
@Rule // @Rule指定被测试的类
public ActivityTestRule<MainActivity> activityActivityTestRule
=new ActivityTestRule<MainActivity>(MainActivity.class);
@Test //@Test指定需测试的功能,方法名随意定
public void changContentTest(){
Matcher<View> matcher= ViewMatchers.withId(R.id.main_change);
//获取R.id.main_change为id的view
ViewInteraction viewInteraction= Espresso.onView(matcher);
ViewAction viewAction= ViewActions.click();
viewInteraction.perform(viewAction);//点击事件
// 简写方式:onView(withId(R.id.main_change)).perform(click());
}
@Test
public void testAdapterView(){
//listView中内容为“2”的item点击( listviwe中数据类型String,所对应的值为“2” )
onData(Matchers.allOf(
Matchers.is(Matchers.instanceOf(String.class)),Matchers.is("2")
) ).perform(click());
//检查上一步点击事件,有没有执行成功
onView(withId(R.id.main_change)).check(matches(withText("2")));
}
}
运行结果:
下一篇: Android Espresso测试Intents,WebView
stackOverFlow上关于Espresso:
http://stackoverflow.com/questions/tagged/android-testing