在不冻结主线程的情况下在Espresso Android中进行延迟测试

问题描述:

我是Espresso测试框架的新手.现在,我有一个任务来测试一些可与异步后端一起使用的应用程序.在第一个活动开始时,某些片段仅在加载后出现.这可能需要几秒钟,所以最简单的方法是等待5到7秒钟.但是,使用IdlingResource会冻结主线程,因此直到等待超时结束后才能加载我的后端数据.

I am new in Espresso testing framework. Now I have a task to test some application which works with async backend. While the first activity starts some fragments appear only after they load. That can take several seconds, so the easiest way is just to wait 5-7 seconds. However using IdlingResource freezes main thread, so my backend data cannot load until waiting timeout is over.

这就是我使用IdlingResource的方式:

That's how I use IdlingResource:

public static class ElapsedTimeIdlingResource implements IdlingResource {
    private final long startTime;
    private final long waitingTime;
    private ResourceCallback resourceCallback;

    ElapsedTimeIdlingResource(long waitingTime) {
        this.startTime = System.currentTimeMillis();
        this.waitingTime = waitingTime;
    }

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

    @Override
    public boolean isIdleNow() {
        long elapsed = System.currentTimeMillis() - startTime;
        boolean idle = (elapsed >= waitingTime);
        if (idle) resourceCallback.onTransitionToIdle();
        return idle;
    }

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

我怎么称呼它:

    long waitingTime = 5000;

    onView(withId(R.id.row_content)).check(matches(isDisplayed())).perform(click());

    IdlingPolicies.setMasterPolicyTimeout(waitingTime * 2, TimeUnit.MILLISECONDS);
    IdlingPolicies.setIdlingResourceTimeout(waitingTime * 2, TimeUnit.MILLISECONDS);

    IdlingResource idlingResource = new ElapsedTimeIdlingResource(waitingTime);
    IdlingRegistry.getInstance().register(idlingResource);

   // .... do some tests

   IdlingRegistry.getInstance().unregister(idlingResource);

如何在不阻塞主线程的情况下延迟测试执行?

How to delay test execution without blocking main thread?

如果您只想等待一段时间,则不需要IdlingResource:

You don't really need an IdlingResource if you just want to wait for an amount of time:

public static ViewAction waitFor(long delay) {
    return new ViewAction() {
        @Override public Matcher<View> getConstraints() {
            return ViewMatchers.isRoot();
        }

        @Override public String getDescription() {
            return "wait for " + delay + "milliseconds";
        }

        @Override public void perform(UiController uiController, View view) {
            uiController.loopMainThreadForAtLeast(delay);
        }
    };
}

并使用它:

onView(withId(R.id.row_content)).check(matches(isDisplayed())).perform(click());   
onView(isRoot()).perform(waitFor(5000);

但是,如果您知道该视图将在一段时间后出现,则可以使用IdlingResource例如:

But if you know the view is going to appear after an amount of time, then you can use an IdlingResource for example:

public static ViewAction waitUntil(Matcher<View> matcher) {
    return actionWithAssertions(new ViewAction() {
        @Override public Matcher<View> getConstraints() {
            return ViewMatchers.isAssignableFrom(View.class);
        }

        @Override public String getDescription() {
            StringDescription description = new StringDescription();
            matcher.describeTo(description);
            return String.format("wait until: %s", description);
        }

        @Override public void perform(UiController uiController, View view) {
            if (!matcher.matches(view)) {
                LayoutChangeCallback callback = new LayoutChangeCallback(matcher);
                try {
                    IdlingRegistry.getInstance().register(callback);
                    view.addOnLayoutChangeListener(callback);
                    uiController.loopMainThreadUntilIdle();
                } finally {
                    view.removeOnLayoutChangeListener(callback);
                    IdlingRegistry.getInstance().unregister(callback);
                }
            }
        }
    });
}

private static class LayoutChangeCallback implements IdlingResource, View.OnLayoutChangeListener {

    private Matcher<View> matcher;
    private IdlingResource.ResourceCallback callback;
    private boolean matched = false;

    LayoutChangeCallback(Matcher<View> matcher) {
        this.matcher = matcher;
    }

    @Override public String getName() {
        return "Layout change callback";
    }

    @Override public boolean isIdleNow() {
        return matched;
    }

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

    @Override public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
        matched = matcher.matches(v);
        callback.onTransitionToIdle();
    }
}

并使用它作为示例:

onView(withId(R.id.row_content)).check(matches(isDisplayed())).perform(click());
onView(withId(R.id.main_content)).perform(waitUntil(isDisplayed()))