When I have a ProgressBar in layouts that are displayed when running some espresso-tests - then I run into:
Caused by: android.support.test.espresso.AppNotIdleException: Looped for 1670 iterations over 60 SECONDS. The following Idle Conditions failed .
What is a nice way to work around this? Found some hackish things but searching for a nice way
If the ProgressBar
is invisible when the test starts, the Drawable
can be replaced with by a custom ViewAction
:
// Replace the drawable with a static color
onView(isAssignableFrom(ProgressBar.class)).perform(replaceProgressBarDrawable());
// Click a button (that will make the ProgressBar visible)
onView(withText("Show ProgressBar").perform(click());
The custom ViewAction
:
public static ViewAction replaceProgressBarDrawable() {
return actionWithAssertions(new ViewAction() {
@Override
public Matcher<View> getConstraints() {
return isAssignableFrom(ProgressBar.class);
}
@Override
public String getDescription() {
return "replace the ProgressBar drawable";
}
@Override
public void perform(final UiController uiController, final View view) {
// Replace the indeterminate drawable with a static red ColorDrawable
ProgressBar progressBar = (ProgressBar) view;
progressBar.setIndeterminateDrawable(new ColorDrawable(0xffff0000));
uiController.loopMainThreadUntilIdle();
}
});
}
I have the same problem. I could not figure out a totally elegant solution, but I will post my approach either.
What I tried to do is to override the indeterminateDrawable
on the ProgressBar. When having a simple drawable no animation takes place and the Espresso test does not ran into the Idle issue.
Unfortunately main
and androidTest
are treated the same. I did not find a way to override the styles for my ProgressBar.
It now ended up in combining some ideas from https://gist.github.com/Mauin/62c24c8a53593c0a605e#file-progressbar-java and How to detect whether android app is running UI test with Espresso.
At first I created to custom ProgressBar classes, one for debug and one for release. The release version only calls the super constructors and does nothing else. The debug version overrides the method setIndeterminateDrawable
. With this I could set a simple drawable instead of the animated one.
Release code:
public class ProgressBar extends android.widget.ProgressBar {
public ProgressBar(Context context) {
super(context);
}
public ProgressBar(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public ProgressBar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
}
Debug code:
public class ProgressBar extends android.widget.ProgressBar {
public ProgressBar(Context context) {
super(context);
}
public ProgressBar(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public ProgressBar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@SuppressWarnings("deprecation")
@Override
public void setIndeterminateDrawable(Drawable d) {
if (isRunningTest()) {
d = getResources().getDrawable(R.drawable.ic_replay);
}
super.setIndeterminateDrawable(d);
}
private boolean isRunningTest() {
try {
Class.forName("base.EspressoTestBase");
return true;
} catch (ClassNotFoundException e) {
/* no-op */
}
return false;
}
}
As you can see I also added a check if my app is running an Espresso test, whereas the class I am searching for is the base of my Espresso tests.
The bad thing is that you have to update all your code to use your custom ProgressBar. But the good thing is that your release code does not have a major impact with this solution.
I have the similar issue. The test failed as early as the first call getActivity(). So the indeterminate drawable of ProgressBar have to be replaced after the activity started.
Application application = (Application)this.getInstrumentation().getTargetContext().getApplicationContext();
application.registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
//not here, it's too early
}
@Override
public void onActivityStarted(Activity activity) {
//find the progressBar in your activity
ProgressBar progressBar = ((ProgressBar) activity.findViewById(R.id.progress_bar));
if(progressBar != null) {
//replace progress bar drawable as not animated
progressBar.setIndeterminateDrawable(new ColorDrawable(0xffff0000));
}
}
@Override
public void onActivityResumed(Activity activity) {
}
@Override
public void onActivityPaused(Activity activity) {
}
@Override
public void onActivityStopped(Activity activity) {
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
});
//Now you can start the activity
getActivity();
Based on Thomas R. solution, another approach is to change the drawable of the ProgressBar in the test, to avoid modifying production code.
Example:
Activity activity = startActivity();
// override progress bar infinite animation with a simple image
ProgressBar progressBar = (ProgressBar) activity.findViewById(R.id.loading_progressbar);
progressBar.setIndeterminateDrawable(activity.getDrawable(android.R.drawable.ic_lock_lock));
// click on the button that triggers the display of the progress bar
onView(withId(R.id.login_button)).perform(click());
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