IdlingResource

闲置资源的力量在于不必等待应用程序的处理(网络,计算,动画等)来完成 sleep(),这会带来瑕疵和/或延长测试运行时间。官方文档可以在这里找到。

履行

实现 IdlingResource 接口时,你需要做三件事:

  • getName() - 返回你的空闲资源的名称。
  • isIdleNow() - 检查你的 xyz 对象,操作等是否空闲。
  • registerIdleTransitionCallbackIdlingResource.ResourceCallback callback) - 提供一个回调,当你的对象转换为空闲时你应该调用它。

现在,你应该创建自己的逻辑并确定应用程序何时处于空闲状态,何时处于空闲状态,因为这取决于应用程序。下面你会看到一个简单的例子,只是为了说明它是如何工作的。在线还有其他示例,但特定的应用程序实现会带来特定的空闲资源实现。

笔记

  • 有一些谷歌的例子,他们把 IdlingResources 放在应用程序的代码中。**不要这样做。**他们可能会把它放在那里只是为了展示它们是如何工作的。
  • 保持代码清洁并保持单一的责任原则取决于你!

让我们说你有一个奇怪的东西活动,并且需要花费很长时间才能加载片段,因此无法从片段中找到资源,从而使 Espresso 测试失败(你应该更改活动的创建方式以及何时更改加快速度)。但无论如何要保持简单,下面的例子展示了它应该是什么样子。

我们的示例空闲资源将获得两个对象:

  • 你需要查找并等待附加到活动的片段的标记
  • 一个 FragmentManager 其用于查找片段对象。
/**
 * FragmentIdlingResource - idling resource which waits while Fragment has not been loaded.
 */
public class FragmentIdlingResource implements IdlingResource {
    private final FragmentManager mFragmentManager;
    private final String mTag;
    //resource callback you use when your activity transitions to idle
    private volatile ResourceCallback resourceCallback;

    public FragmentIdlingResource(FragmentManager fragmentManager, String tag) {
        mFragmentManager = fragmentManager;
        mTag = tag;
    }

    @Override
    public String getName() {
        return FragmentIdlingResource.class.getName() + ":" + mTag;
    }

    @Override
    public boolean isIdleNow() {
        //simple check, if your fragment is added, then your app has became idle
        boolean idle = (mFragmentManager.findFragmentByTag(mTag) != null);
        if (idle) {
            //IMPORTANT: make sure you call onTransitionToIdle
            resourceCallback.onTransitionToIdle();
        }
        return idle;
    }

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

现在你已经编写了 IdlingResource,你需要在某处使用它吗?

用法

让我们跳过整个测试类设置,看看测试用例的外观如何:

@Test
public void testSomeFragmentText() {
    mActivityTestRule.launchActivity(null);
   
    //creating the idling resource
    IdlingResource fragmentLoadedIdlingResource = new FragmentIdlingResource(mActivityTestRule.getActivity().getSupportFragmentManager(), SomeFragmentText.TAG);
    //registering the idling resource so espresso waits for it
    Espresso.registerIdlingResources(idlingResource1);
    onView(withId(R.id.txtHelloWorld)).check(matches(withText(helloWorldText)));

    //lets cleanup after ourselves
    Espresso.unregisterIdlingResources(fragmentLoadedIdlingResource);
}

结合 JUnit 规则

这不难; 你还可以以 JUnit 测试规则的形式应用空闲资源。例如,假设你有一些包含 Volley 的 SDK,并且你希望 Espresso 等待它。你可以创建一个 JUnit 规则,而不是遍历每个测试用例或在设置中应用它,而只需编写:

@Rule
public final SDKIdlingRule mSdkIdlingRule = new SDKIdlingRule(SDKInstanceHolder.getInstance());

既然这是一个例子,不要把它视为理所当然; 这里的所有代码都是虚构的,仅用于演示目的:

public class SDKIdlingRule implements TestRule {
    //idling resource you wrote to check is volley idle or not
    private VolleyIdlingResource mVolleyIdlingResource;
    //request queue that you need from volley to give it to idling resource
    private RequestQueue mRequestQueue;

    //when using the rule extract the request queue from your SDK
    public SDKIdlingRule(SDKClass sdkClass) {
        mRequestQueue = getVolleyRequestQueue(sdkClass);
    }

    private RequestQueue getVolleyRequestQueue(SDKClass sdkClass) {
        return sdkClass.getVolleyRequestQueue();
    }

    @Override
    public Statement apply(final Statement base, Description description) {
        return new Statement() {
            @Override
            public void evaluate() throws Throwable {
                //registering idling resource
                mVolleyIdlingResource = new VolleyIdlingResource(mRequestQueue);
                Espresso.registerIdlingResources(mVolleyIdlingResource);
                try {
                    base.evaluate();
                } finally {
                    if (mVolleyIdlingResource != null) {
                        //deregister the resource when test finishes
                        Espresso.unregisterIdlingResources(mVolleyIdlingResource);
                    }
                }
            }
        };
    }
}