阅读 1395

Android使用Espresso进行UI自动化测试

谷歌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!")));
    }
}
复制代码
  1. 首先需要在测试用例类的类体前添加@RunWith的注解,并设置测试运行平台为AndroidJUnit4
  2. 如果允许测试需要较大消耗,可以使用@LargeTest注解
  3. 设置ActivityTestRule用来指明被测试的Activity,使用@Rule注解
  4. 测试方法必须以 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的介绍大概就是这些了,希望大家多提建议,一起进步。

获取更多精彩内容,关注我的微信公众号——Android机动车

关注下面的标签,发现更多相似文章
评论