ProgressBar animations with Espresso

Halil Ozercan
ProAndroidDev
Published in
4 min readSep 18, 2020

--

It is not uncommon that once you run into an issue with Intrumentation Testing in Android, you’d feel like the loneliest person in the world. This might be an unforeseen effect of abundance of Android knowledge that exists on the Internet. Whatever you search for, there is always a StackOverflow answer or a Medium article that exactly describes what you need to do. However, this doesn’t hold for testing. My assumption is that this trend would hold in many other areas of Software Engineering. Although TDD is being popularized, it is still fairly rare, especially among one-developer teams which is unfortunately very common in mobile teams.

Problem: Animations in Espresso

Espresso has quite a bit obsession with idleness. Any action or assertion on views first require everything to settle on UI. In other words, if anything is still moving on your screen, Espresso will happily wait before executing any command. This doesn’t necessarily cause a problem if we are talking about transition or window animations that take fraction of a second. However, what happens if we want to show a ProgressBar or some sort of drawable that is animated when the screen goes into a loading state.

Wow, that’s not pretty. Espresso looped around 10k times and finally gave up after a minute. What can we do to circumvent this issue and tell the Espresso that the screen is actually idle?

Proposed Solutions

Let’s do a quick Google search and see what we find

  1. Replace the Progressbar right after test starts

There is no accepted answer so we can start with the most upvoted one. It simply suggests that if your Progressbar is invisible at the start of the test(yes, in our case), just change its drawable with something static so it does not animate.

Although this may sound very legit and works, it doesn’t feel right to me. First of all, there is a big assumption that my test will start without showing the progressbar. When this doesn’t hold, the whole approach fails. Secondly, it requires me to mess with UI during testing. Not before, not after, but exactly at the time of running the test. This hack sounds innocent but violates some of the principles of UI testing.

2. Override Progressbar for Debug builds (or for any flavor)

This was the first thing that also I thought of. Create a new ProgressBar, extending from the original ProgressBar, and stop the animation in Debug builds.

I wouldn’t recommend directly doing this because debug builds are intended to be used by developers during hands on testing or development time, in which animations should not be disabled for any reason. Luckily, we have a flavor of our app that is intended for instrumentation testing. Instead of debug builds, we can disable the animations according to this flavor.

Again like the first one, this approach works if you are OK with it. My concern is that changing production code and adding logic to circumvent a testing issue might cause unpredictable side-affects. Also, it’s again in violation of core principles of UI testing.

3. Others

As far as I checked, all the other solutions are derivatives of the first two that we have discussed. Either intercept during testing and alter the offending drawable, or change production code to account for testing. In some cases, people have given up completely and maybe stopped testing these screens.

In the above answer, the author suggested the use of UiAutomator. If Espresso can’t handle idleness of a progress drawable, we can simply take the control from Espresso and give it to another library with similar functionality.

Another Possibility and Other Animated Drawables

Until now, I haven’t even mentioned my original problem. To our bad luck, the problematical view is not even a ProgressBar or derivative of it. A custom button view that shows a Progress animation when its state switches to Loading . The drawable inside it is private so the first solution is out the window. The second solution is also out-ruled because we don’t want to interfere with production code logic.

Fortunately, there is something missing in Stackoverflow answers. Do you remember a line that 99% exists in your build.gradle file?

testInstrumentationRunner "com.example.hello.HelloTestRunner"

This custom test runner has incredible capabilities when it comes to meddling with your app during testing. For example you can override every activity’s oncreate . There, you can utilize custom Layout Inflater Factories to your liking.

Now, we can just swap any view with another stub implementation that simply disables animations during testing.

This method doesn’t care about your actual implementation. It can be thought of as a TestRule . When your test starts, your activities’ inflater factories are swapped with this class. The view classes that you are not interested in modifying will keep inflating as always but the ones you want to swap out will be changed with the new Stub implementations. In these stub implementations you can disable animations and do whatever you want as long as you are respecting the fundamental rules of instrumentation testing.

--

--