AndroidPub
Published in

AndroidPub

Unit Testing animation results using Robolectric

Robolectric is a great tool to test classes that depend on Android views and classes properly. I use Robolectric heavily to test custom view logic.

I wrote a custom view to mimic TextInputLayout with some added app specific features. I also did the hint to label and label to hint animation in the custom view to mimic this.

Take from https://material.io/design/components/text-fields.html#anatomy

So I have a showLabel() method which animates hint to label text and hideLabel() method which animates label to text.

So our naive test class for this view would look like this.

As mentioned in the comment, the assertions would fail. Why? Because the show/hide label method implementations doesn’t set the text synchronously. It sets the text only after the animation is over and the animation happens for about 250 milliseconds in our implementation.

So how do we make our assertion calls wait until the animation finishes? Google search with various keywords results in some number of solutions. Let me save you some clicks and list them here.

  1. Scheduler().advanceBy(250, TimeUnit.MILLISECONDS)
  2. Robolectric.getForegroundThreadScheduler().advanceBy(250,TimeUnit.MILLISECONDS)z
    Robolectric.flushForegroundThreadScheduler()
  3. Shadows.shadowOf(Looper.getMainLooper()).idleFor(250,TimeUnit.MILLISECONDS)
  4. Thread.sleep(250)

Mind you, none of these works. I don’t know the exact reason for the first three, but I can tell you why last one won’t work. Thread.sleep() blocks the main thread, which means our animation is also blocked for 250 milliseconds, and our label/hint won’t be set, hence the assertion would fail, again.

So what is the solution?

We need to add one line to our test class, that is @LooperMode(LooperMode.Mode.PAUSED)

And after showLabel/hideLabel method call, we should call Shadows.shadowOf(Looper.getMainLooper()).idle() and assert call should follow. The idle() call would basically runs all posted tasks in the main looper.

Now our test class becomes like this.

tl;dr

  1. Add @LooperMode(LooperMode.Mode.PAUSED) for the test class
  2. Add Shadows.shadowOf(Looper.getMainLooper()).idle() before assertion to run all pending animations

--

--

The (retired) Pub(lication) for Android & Tech, focused on Development

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Muthu Raj

Muthu Raj

Android Developer at Zoho Corp