(4.5.5.6)Espresso的进阶: IdlingResource

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状态的变化。

官方示例

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

自定义 示例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()

@Override
public String getName() {
  return IntentServiceIdlingResource.class.getName();
}

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

@Override
public 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中执行注销

@Before
public void registerIntentServiceIdlingResource() {
    Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
    idlingResource = new IntentServiceIdlingResource(instrumentation.getTargetContext());
    Espresso.registerIdlingResources(idlingResource);
}

@After
public 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
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值