Android利用Espresso进行UI自动化测试的方法详解

为什么需要UI自动化测试?

我有一个观点,对于重复的工作,那么程序都是可以代替的,我想这是作为一个程序员的一个基本素养(能偷懒的绝不干活)。UI自动化测试就是为了应付一些重复的工作,比如说测试某个功能,那么从应用点击,再经过一系列的点击页面才能到达这个页面,然后进行测试,那么我们是不是可以写段代码让app自动跑起来,自动来到那个界面进行测试呢?答案是肯定的,这就是本文所要说的自动化测试。

引言

谷歌2013年的时候开源了espress,谷歌的思路是,等到它足够成熟和稳定以后,将其迁移到Android SDK中,以此可见对他的重视。Google使用Espresso测试了他们自己的超过30个应用程序,包括G+、Maps和Drive。

Espresso测试是非常容易实现的,由三步构成:

  • ViewMachers:寻找用来测试的View。
  • ViewActions:发送交互事件。
  • 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

Espresso框架是google官方大力推荐的一套测试框架,所以无论如何都要学习一下的.另外,自Android Studio2.2版本开始,google就为Espresso框架内置了一个图形化界面,用来自动生成单元测试代码。

接下来一起写一demo测试,深入了解Espresso。

准备

支持Espresso:

dependencies {
 ...
 testCompile 'junit:junit:4.12'
 androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2', {
 exclude group: 'com.android.support', module: 'support-annotations'
 }
}

在dependencies中添加,一般默认会有testCompile 'junit:junit:4.12',所以我们只需添加另一句即可。

defaultConfig{
 ...
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}

在defaultConfig中添加如上语句,支持测试运行。

创建Test类

特别注意,该类应在androidTest文件夹下


  • androidTest:进行与Android相关(如调用Android设备等)测试;
  • test:进行简单的只涉及java SE相关的测试。

举个简单例子:

@RunWith(AndroidJUnit4.class)
@LargeTest
public class MainActivityInstrumentationTest {
 @Rule
 public ActivityTestRule mActivityRule = new ActivityTestRule<>(
   MainActivity.class);
 @Test
 public void sayHello(){
  onView(withText("Say hello!")).perform(click());
  onView(withId(R.id.textView)).check(matches(withText("Hello, World!")));
 }
}
  • 首先需要在测试用例类的类体前添加@RunWith的注解,并设置测试运行平台为AndroidJUnit4
  • 如果允许测试需要较大消耗,可以使用@LargeTest注解
  • 设置ActivityTestRule用来指明被测试的Activity,使用@Rule注解
  • 测试方法必须以 test 开头,并且使用@Test注解(否则会报找不到方法异常)

@Rule

@Rule
public ActivityTestRule mTestRule = new ActivityTestRule<>(MainActivity.class);

这句话就定义了一个测试规则,可以看到构造方法的参数里指定了一个 MainActivity.class, 具体的体现就是当你运行这段测试代码时,app将会直接打开 MainActivity界面然后进行你所定义的测试用例。 所以当你想直接测试某个界面时,你可以把那个界面填到这个参数里,这样就直接打开你指定的界面进行测试了。

@Test

@Test
public void testLogin() {
 ...
}

定义一个测试方法,当你的测试类运行时,所执行的代码就是Test注解下的方法(Espresso还提供了其他的一些注解: 比如@After,@Before等,具体的用法可以去我上面写的android官网上查看),当然上面那段代码对应的就是testLogin测试方法,testLogin方法里所定义的就是要测试的内容。

ViewMachers 查找View

使用onView方法找到view:其中参数可以是withId(通过资源id查找),withText(通过显示内容查找)有多个约束条件时,可以使用allOf  如allOf(withText("Hello") ,withId(R.id.hello))

注意:

  • 无论是通过withId()找控件还是通过withText()找控件,或者其他方式比如 withClassName() ,withResourceName() ,withTagKey()等方法,都要一定保证你所找的控件在当前页面确实存在且可见。
  • 如果要测试AdapterView ,比如 ListView 或GridView等,使用上面的onView()方法是无效的,因为AdapterView的布局item是动态呈现的,没法直接指定,所以当要测试AdapterView时,请把onView()方法换成onData() 方法,与onView()方法返回ViewInteraction类似,onData()方法返回DataInteraction,二者用法基本都是一样的。

ViewActions 执行事件

对View的操作:perform()方法  方式是onView(...).perform() 。也可以执行多个操作在一个perform中如:perform(click(),clearText()) 。

所有的操作都有一个前提 ———— 就是要执行的view必须在当前界面上显示出来(有且可见)。

方法名 含义
click() 点击view
clearText() 清除文本内容
swipeLeft() 从右往左滑
swipeRight() 从左往右滑
swipeDown() 从上往下滑
swipeUp() 从下往上滑
click() 点击view
closeSoftKeyboard() 关闭软键盘
pressBack() 按下物理返回键
doubleClick() 双击
longClick() 长按
scrollTo() 滚动
replaceText() 替换文本
openLinkWithText() 打开指定超链

ViewAssertions 检验结果

使用check()方法来检查View是否符合我们的期望: onView(...).check() 检查view中是否含有文本“hello”              check(matches(withText("hello")))

看下我写的示例

我们基本所有的app都有登录功能,都需要呼入用户名和密码,那么在点击登录之前需要对用户名和密码进行非空、格式等验证。

以下示例我们点击登录按钮时,首先对输入的用户名和密码进行验证,验证不通过在TextView上显示对应原因,验证没有问题显示“登录成功”。

Activity界面及逻辑

 @Override
 public void onClick(View view) {
  if (view.getId() == R.id.bt_login) {
   login();
  }
 }
 /**
  * 去登录
  */
 private void login() {
  String name = et_name.getText().toString().trim();
  String pwd = et_pwd.getText().toString().trim();
  if (TextUtils.isEmpty(name)) {
   tv_login_result.setText("用户名为空");
   return;
  }
  if (name.length() < 6 ) {
   tv_login_result.setText("用户名格式错误");
   return;
  }
  if (TextUtils.isEmpty(pwd)) {
   tv_login_result.setText("密码为空");
   return;
  }
  if (pwd.length() < 6 ) {
   tv_login_result.setText("密码格式错误");
   return;
  }
  tv_login_result.setText("登录成功");
 }

其他代码忽略。

@RunWith(AndroidJUnit4.class)
@LargeTest
public class MainActivityTest {
 private String[] names = {"", "a", "123123"};
 private String[] pwds = {"", "a", "123123"};
 @Rule
 public ActivityTestRule mTestRule = new ActivityTestRule<>(MainActivity.class);
 @Before
 public void init() {
  Log.e("TAG", "init: ");
 }
 @Test
 public void testLogin() {
  // 不做任何输入,直接点击登录
  onView(allOf(withId(R.id.bt_login), isDisplayed())).perform(click());
  onView(allOf(withId(R.id.tv_login_result), isDisplayed())).check(matches(withText("用户名为空")));
  // 用户名是空,点击登录
  onView(allOf(withId(R.id.et_name), isDisplayed())).perform(replaceText(names[0]), closeSoftKeyboard());
  onView(allOf(withId(R.id.bt_login), isDisplayed())).perform(click());
  onView(allOf(withId(R.id.tv_login_result), isDisplayed())).check(matches(withText("用户名为空")));
  // 用户名格式错误,点击登录
  onView(allOf(withId(R.id.et_name), isDisplayed())).perform(replaceText(names[1]), closeSoftKeyboard());
  onView(allOf(withId(R.id.bt_login), isDisplayed())).perform(click());
  onView(allOf(withId(R.id.tv_login_result), isDisplayed())).check(matches(withText("用户名格式错误")));
  // 用户名和密码都正确,点击登录
  onView(allOf(withId(R.id.et_name), isDisplayed())).perform(replaceText(names[2]), closeSoftKeyboard());
  onView(allOf(withId(R.id.et_pwd), isDisplayed())).perform(replaceText(pwds[2]), closeSoftKeyboard());
  onView(allOf(withId(R.id.bt_login), isDisplayed())).perform(click());
  onView(allOf(withId(R.id.tv_login_result), isDisplayed())).check(matches(withText("登录成功")));
 }
}

这里我们事先定义了一些测试数据,使用Espresso进行模拟各种情况输入和点击,测试是否符合我们的预期:

对Espresso的介绍大概就是这些了,希望大家多提建议,一起进步。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对呐喊教程的支持。

声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:notice#nhooo.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。