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

非洲赵子龙 for 安卓Espresso菜刀队 · 2018年03月22日 · 2477 次阅读

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 入门 及 异步测试

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册