Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Delay test in Espresso Android without freezing main thread

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.

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;
    }
}

That how I call it:

    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?

like image 540
Dmitry K. Avatar asked Oct 15 '18 14:10

Dmitry K.


People also ask

How do I record espresso test on android?

Record UI interactions To start recording a test with Espresso Test Recorder, proceed as follows: Click Run > Record Espresso Test. In the Select Deployment Target window, choose the device on which you want to record the test. If necessary, create a new Android Virtual Device.

How do you assert espresso?

check is a method which accepts an argument of type ViewAssertion and do assertion using passed in ViewAssertion object. matches(withText(“Hello”)) returns a view assertion, which will do the real job of asserting that both actual view (found using withId) and expected view (found using withText) are one and the same.


2 Answers

So I have a splashscreen fragment that transactions to a the fragment I am testing after a delay. @Aarons answer worked for.

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

Kotlin waitfor():

fun waitFor(delay: Long): ViewAction? {
    return object : ViewAction {
        override fun getConstraints(): Matcher<View> = isRoot()
        override fun getDescription(): String = "wait for $delay milliseconds"
        override fun perform(uiController: UiController, v: View?) {
            uiController.loopMainThreadForAtLeast(delay)
        }
    }
}
like image 150
Haider Malik Avatar answered Oct 05 '22 16:10

Haider Malik


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);
        }
    };
}

And use it:

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

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

And use it for example:

onView(withId(R.id.row_content)).check(matches(isDisplayed())).perform(click());
onView(withId(R.id.main_content)).perform(waitUntil(isDisplayed()))
like image 41
Aaron Avatar answered Oct 05 '22 17:10

Aaron