(4.5.5.6)Espresso的进阶: IdlingResource

来源:互联网 发布:淘宝八大山人书法全集 编辑:程序博客网 时间:2024/06/09 14:05

Espresso 的核心是它可以与待测应用无缝同步测试操作的能力。默认情况下,Espresso 会等待当前消息队列中的 UI 事件执行(默认是 AsyncTask)完毕再进行下一个测试操作。这应该能解决大部分应用与测试同步的问题。

然而,应用中有一些执行后台操作的对象(比如与网络服务交互)通过非标准方式实现;例如:直接创建和管理线程,以及使用自定义服务。

此种情况,我们建议你首先提出可测试性的概念,然后询问使用非标准后台操作是否必要。某些情况下,可能是由于对 Android 理解太少造成的,并且应用也会受益于重构(例如,将自定义创建的线程改为 AsyncTask)。然而,某些时候重构并不现实。庆幸的是 Espresso 仍然可以同步测试操作与你的自定义资源。

这是基于idling的概念:Espresso等待app处于idle状态,才会执行下个动作和检查下个断言.

那么app处于idle状态是什么意思?Espresso检查下面几个场景:

  • 在当前消息队列中没有UI事件;
  • 在默认的AsyncTask线程池没有任务;

但是,如果app以其他方式执行长时间运行操作,Espresso不知道如何判断这些操作已经完成。如果是这样的话,可以通过编写自定义的IdelingResource来通知Espresso的等待时间。

idlingResource

它是一个简单的接口:
它代表了被测应用程序的资源,这个资源在测试执行时可以在后台异步工作。
接口定义了三个方法:

  • getName():必须返回代表idling resource的非空字符串;

  • isIdleNow():返回当前idlingresource的idle状态
    如果返回true,onTransitionToIdle()上注册的ResourceCallback必须必须在之前已经调用;

  • registerIdleTransitionCallback:通常此方法用于存储对回调的引用来通知idle状态的变化。

官方示例

需要入侵源代码,不做过多描述

  • Espresso IdlingResource 测试延时操作的使用

自定义 示例1

问题:
点击界面某个按钮后,新出现的页面会存在一个progressDialogFragment进行网络的请求数据,直到网络异常后者请求成功才会消失

其实这个时候如何不做任何处理的话,很明显,Espresso在点击按钮后的其他操作都会失败,它肯定会报错说NoViewsMatching

所以我们需要自定义一个IdlingResource:

public class ProgressFramentIdlingResource implements IdlingResource {    private FragmentManager fragmentManager;    private ResourceCallback resourceCallback;    public ProgressFramentIdlingResource(FragmentManager fg) {        fragmentManager = fg;    }    @Override    public String getName() {        return ProgressFramentIdlingResource.class.getName();    }    @Override    public boolean isIdleNow() {        //通过当前页面的fragment 是否存在以及可见,来判断。        for (Fragment fragment:fragmentManager.getFragments()) {            if(fragment != null && fragment.isVisible()) {                resourceCallback.onTransitionToIdle();                return false;            }        }        return true;    }    @Override    public void registerIdleTransitionCallback(ResourceCallback callback) {        resourceCallback = callback;    }}

上面的方法是没问题的,但是问题来了,如何才能够获取到framgentManager呢。正常情况下我们只是需要activity.getSupportFragmentManager就可以解决,但是我们在Espresso中获取到的activity的对象却是上一个界面的对象,所以我们还得去有方法能够获取到当前的Acitivity。还是伟大的google帮忙了

public Activity getActivityInstance(){        try {            testRule.runOnUiThread(new Runnable() {                public void run() {                    Collection<Activity> resumedActivities = ActivityLifecycleMonitorRegistry.getInstance().getActivitiesInStage(Stage.RESUMED);                    for (Activity act: resumedActivities){                        Log.d("Your current activity: ", act.getClass().getName());                        currentActivity = act;                        break;                    }                }            });        } catch (Throwable throwable) {            throwable.printStackTrace();        }        return currentActivity; }

上面的方法就解决了获取当前Activity的问题了,具体的使用

IdlingResource idlingResource = new ProgressFramentIdlingResource(((UpdateSubjectActivity)getActivityInstance()).getSupportFragmentManager());Espresso.registerIdlingResources(idlingResource);****Espresso.unregisterIdlingResources(idlingResource);

自定义 示例2

假设你使用IntentService来做一些长时间运算,然后通过broadcast将结果返回给activity。我们希望Espresso一直等到结果返回,才来验证界面显示正确。

为了实现IdlingResource,需要重写3个函数:getName(),registerIdleTransitionCallback(),isIdleNow()

@Overridepublic String getName() {  return IntentServiceIdlingResource.class.getName();}@Overridepublic void registerIdleTransitionCallback(ResourceCallback resourceCallback) {    this.resourceCallback = resourceCallback;}@Overridepublic boolean isIdleNow() {    boolean idle = !isIntentServiceRunning();    if (idle && resourceCallback != null) {        resourceCallback.onTransitionToIdle();    }    return idle;}private boolean isIntentServiceRunning() {    ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);    for (ActivityManager.RunningServiceInfo info : manager.getRunningServices(Integer.MAX_VALUE)) {        if (RepeatService.class.getName().equals(info.service.getClassName())) {            return true;        }    }    return false;}

idle逻辑是在isIdleNow()实现的。在这个例子中,我们通过查询ActivityManager来检查IntentService是否正在运行。如果IntentService停止运行,我们调用resourceCallback.onTransitionToIdle()来通知Espresso

为了让Espresso等待自定义的idling resource,你需要注册它。在测试代码的@Before方法中执行注册,在@After中执行注销

@Beforepublic void registerIntentServiceIdlingResource() {    Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();    idlingResource = new IntentServiceIdlingResource(instrumentation.getTargetContext());    Espresso.registerIdlingResources(idlingResource);}@Afterpublic void unregisterIntentServiceIdlingResource() {    Espresso.unregisterIdlingResources(idlingResource);}

自定义 示例3

public class MainActivityIdlingResource implements IdlingResource {    private ResourceCallback mCallback;    private final long startTime;    private final long waitingTime;    ActivityTestRule testRule;    public MainActivityIdlingResource(long waitingTime, ActivityTestRule testRule)    {        this.startTime = System.currentTimeMillis();        this.waitingTime = waitingTime;        this.testRule =  testRule;    }    @Override    public String getName() {        return MainActivityIdlingResource.class.getSimpleName();    }    @Override    public boolean isIdleNow() {        //当Activity 出现时        if(ActivityUtils.getActivityInstance(testRule).getClass().getSimpleName().equals("MoaFragmentTabActivity"))        {            mCallback.onTransitionToIdle();            System.out.println("打印");            return true;        }        return false;    }    @Override    public void registerIdleTransitionCallback(ResourceCallback callback) {        this.mCallback=callback;    }}
        IdlingResource idlingResource1=new MainActivityIdlingResource(1000,  mActivityTestRule);        Espresso.registerIdlingResources(idlingResource1);//等待主界面后执行后面的代码        ...        Espresso.unregisterIdlingResources(idlingResource1); //等代码调试完记得关闭

自定义 示例4

public class ListAdapterIdlingResource implements IdlingResource {    private IdlingResource.ResourceCallback mCallback;    private final long startTime;    private final long waitingTime;    private ListView listView;    public  ListAdapterIdlingResource(long waitingTime,ListView listView)    {        this.startTime = System.currentTimeMillis();        this.waitingTime = waitingTime;        this.listView=listView;    }    @Override    public String getName() {        return ListAdapterIdlingResource.class.getSimpleName();    }    @Override    public boolean isIdleNow() {        //当网络数据加载完,才设置适配器,故可以通过适配器是否为空值来判断其异步数据加载是否完成        if(listView.getAdapter().getCount() != 0)        {            mCallback.onTransitionToIdle();            System.out.println("打印");            return true;        }        return false;        //通过时间来限制其异步加载        /*long elapsed = System.currentTimeMillis() - startTime;        boolean idle = (elapsed >= waitingTime);        if (idle) {            System.out.println("打印");            mCallback.onTransitionToIdle();        }        return idle;*/    }    @Override    public void registerIdleTransitionCallback(ResourceCallback callback) {        this.mCallback=callback;    }}
        IdlingResource idlingResource = new ListAdapterIdlingResource(1000, (((PullListView) ActivityUtils.getActivityInstance(mActivityTestRule).findViewById(R.id.pull)).getRefreshableView()));        //等待后台ListView加载完数据后执行后面的代码        ...        //释放对其异步空闲处理类        Espresso.unregisterIdlingResources(idlingResource);
0 0
原创粉丝点击