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?
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.
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.
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)
}
}
}
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()))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With