在Espresso Android中延迟测试而不冻结主线程

时间:2018-10-15 14:03:20

标签: java android testing android-espresso qa

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

这就是我使用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);

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

2 个答案:

答案 0 :(得分:0)

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

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例如:

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()))

答案 1 :(得分:0)

因此,我有一个启动画面片段,经过延迟后,该片段会转换为我正在测试的片段。 @Aarons答案适用。

onView(isRoot()).perform(waitFor(5000))

Kotlin waitfor():

fun waitFor(delay: Long): ViewAction {
    return object : ViewAction {
        override fun perform(uiController: UiController?, view: View?) {
            uiController?.loopMainThreadForAtLeast(delay)
        }

        override fun getConstraints(): Matcher<View> {
            return isRoot()
        }

        override fun getDescription(): String {
            return "wait for " + delay + "milliseconds"
        }
    }
}