其他测试框架 Espresso - 详解 Espresso 的 IdlingResource 异步测试

非洲赵子龙 for 安卓Espresso菜刀队 · March 22, 2018 · 2346 hits

1. 是什么?
是可以帮你做异步测试的支持包,简单粗暴的可以理解为不需要再写傻傻的Thread.sleep(毫秒)了.
2. 怎么做?
取决于你的业务场景.
注意:后续描述仅以Native场景为例—检查点之前和检查点之后操作的控件对象不包含webview
基本上可以粗略的分为两大场景:1)有页面跳转的-跳转之后,之前页面的页面控件就找不到了,出现了新的页面及其控件集; 2)没有页面跳转的-页面控件没有变化,只是其某些属性发生了变化(比如text属性值)

  • 有页面跳转的

示例业务逻辑:点击主页面的tv_main_username,会有一个弹框(与主页面的布局可视性互斥),输入正确的帐密后,点击登录按钮,弹框消失,主页面重新渲染(之前可能tv_main_username是匿名,现在变成一个存在的账户名)
这个稍微复杂一点,但是懂了之后也很简单:在测试主线程的任何地方传一个会消失的控件(这个控件可以通过当前的Activity的某个方法或者属性暴露出来, 注意:弹框的方式可以有很多种实现方式,要看开发怎么实现的,通常的方式有3种:单独的activity,一个fragment或者 一个Dialog。要根据具体情况做具体调用!)或者把控件的状态传给idlingResource,示例代码如下(笔者项目的APP是以Dialog方式实现的):

MainActivity.java

......
//为了方便自动化测试,将此Dialog暴露出来
public LoginDialog getLoginDialog() {
return loginDialog;
}
private LoginDialog loginDialog;
......

MainIdlingResource.java可以放在项目的任何地方,只要你能调的到(最好是放在测试支撑包-比如取个名字叫TestHelper下面,便于统一管理)

package com.*.*;

import android.support.test.espresso.IdlingResource;
//如果是Fragment实现方式就用下面两个包
//import android.support.v4.app.Fragment;
//import android.support.v4.app.FragmentManager;

import *.*.*.packageupdate.filedownloader.base.Log;

public class MainIdlingResource implements IdlingResource {
// private final boolean idle;
private final MainActivity mActivity;
private ResourceCallback resourceCallback;
MainIdlingResource(MainActivity mActivity){
// this.idle = idle;
this.mActivity = mActivity;
}
@Override
public String getName() {
return "MainIdlingResource";
}
@Override
public boolean isIdleNow() {
if (resourceCallback != null) {
// boolean idle = (manager.findFragmentByTag(tag) == null);
boolean idle = mActivity.getLoginDialog().isShowing();
//如果木有在显示了,那么说明登录框已经消失了,登录完毕了
if (!idle) {
Log.i("DDD",idle+"可以检查了");
resourceCallback.onTransitionToIdle();
return true;
}
else{
Log.i("DDD",idle+"还不能检查");
return false;
}
}
else
{
Log.i("AAA","EEE");
return true;
}
}


@Override
public void registerIdleTransitionCallback(ResourceCallback resourceCallback) {
this.resourceCallback = resourceCallback;
}
}

测试代码

@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@RunWith(AndroidJUnit4.class)
public class Login{
@Rule
public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule<>(
MainActivity.class);
public void loginSuccess(){
//看弹框的实现方式,如果是Fragment类型的,就用下面的 - 传一个FragmentManager进去,用它判断其承载的子控件是否存在
//IdlingResource idlingResource = new MainIdlingResource(mActivityRule.getActivity().getSupportFragmentManager());
//如果是Dialog,实现方式,则直接通过Activity找Dialog然后一定要在MainIdlingResource里判断它是否showing来回调是否放开测试主进程
onView(withId(R.id.tv_main_username)).perform(click());
onView(withId(R.id.et_username)).perform(clearText());
//把Activity实例传过去,然后在IdlingResource里面找出dialog并判断是否还在显示
IdlingResource idlingResource = new MainIdlingResource(mActivityRule.getActivity());
onView(withId(R.id.et_username)).perform(typeText("............"));
onView(withId(R.id.et_password)).perform(clearText());
onView(withId(R.id.et_password)).perform(typeText("........."));
onView(withId(R.id.bt_user_login)).perform(click());

IdlingRegistry.getInstance().register(idlingResource);

onView(withId(R.id.tv_main_username)).check(matches(withText("Hello")));
//检查完毕之后,立马注销idlingResource
IdlingRegistry.getInstance().unregister(idlingResource);
}
}
  • 页面不跳转的-只有某个控件会刷新

示例业务逻辑:页面有一个textview控件,在一段时间后,会从空文本(text)属性更新为“测试异步操作”。
这个就简单很多,示例代码:

MainIdlingResource.java

package com.yu.espressotest;

import android.support.test.espresso.IdlingResource;
import android.text.TextUtils;
import android.widget.TextView;

/**
* Created by yu on 2017/6/28.
* 异步通知
*/


public class MainIdlingResource implements IdlingResource{

private ResourceCallback mCallback;
private TextView tvResult;

// 构造方法传入 tvResult 文本控件,用来判断异步是否完成
public MainIdlingResource(TextView textView){
this.tvResult = textView;
}

// 返回一个名字 一般用于日志打印
@Override
public String getName() {
return "MainIdlingResource";
}

//通知 Espresso 异步操作是否完成
@Override
public boolean isIdleNow() {
if (mCallback != null) {

//当 tvResult 文字不为空的时候表示异步完成
if (!TextUtils.isEmpty(tvResult.getText())) {
// 这里一定要通知 Espresso 切换为空闲状态
mCallback.onTransitionToIdle();
return true;
}else {
return false;
}

}else {
return true;
}
}

@Override
public void registerIdleTransitionCallback(ResourceCallback callback) {
mCallback = callback;
}
}
@RunWith(JUnit4.class)
@LargeTest
public class MainActivityTest {

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

//异步测试
@Test
public void idlingTest() throws InterruptedException {

//不做异步处理 会报错 因为此时 TextView 中的文字还为空串
//Thread.sleep(6000);//这很危险 可能会导致 ANR
//onView(withId(R.id.tv_result)).check(matches(withText("测试异步操作")));

//使用 IdlingResource 做异步通知
IdlingResource idlingResource = new MainIdlingResource((TextView) mActivity.getActivity().findViewById(R.id.tv_result));

//注册 IdlingResources , 之后的 UI 操作会被阻塞(非 UI 操作之前的代码,好像仍不会阻塞)
Espresso.registerIdlingResources(idlingResource);

//异步完成检查结果是否正确
onView(withId(R.id.tv_result)).check(matches(withText("测试异步操作")));

//取消 IdlingResource 注册
Espresso.unregisterIdlingResources(idlingResource);

}

Look! 我们没有花任何多余的时间在无意义的等待上,整个测试过程,可以说把效率发挥到了极致!

有疑问请留言,知无不言.

下一步就是Hybird类型的互相跳转问题了.待续~

参考文献:
chiuki/espresso-samples
Espresso入门 及 异步测试

需要 Sign In 后方可回复, 如果你还没有账号请点击这里 Sign Up