Have you ever done some testing using Espresso and was baffled that you need to manually wait for some UI elements to be visible? I thought testing is so simple, you just tell what to click and what should appear. Brutal reality kicks in and you try to make it easy using IdlingResource, which makes it overly complicated.

Testing on Android gets better - there is no denying this. But at the same time, sometimes it is lacking so many basic functionalities, that it hurts. Clear app data between tests? Wait for view or activity to be visible without Thread.sleep()?

When you ask this question (like on StackOverflow here and here) you usually get one response :

use IdlingResource

and I find this to be overkill in many cases.

As a quick recap - in general Espresso tries to be smart and automatically waits for many things to finish - stuff on UI thread, AsyncTasks (seriously, who uses them nowadays?) and IdlingResources themselves. Out of these three, from tests you can usually easily influence IdlingResources.

But it’s just soooo complicated:

  • It is recommended to modify production code for testing purposes:

    When adding idling resources into your app, we highly recommend performing only the registration and unregistration operations in your tests and placing the idling resource logic itself in your app’s production code. Although you create the unusual situation of using a test-only interface in production code by following this approach, you can wrap idling resources around code that you already have, maintaining your app’s APK size and method count.

    While I don’t say that modifying code for tests purposes is always bad, you should at least try to avoid it.

  • You need to register IdlingResource and then remember to unregister it - more code, more places to make mistakes
  • Condition is checked only once every 5 seconds, which is terrible! It’s not a big deal with a few tests, but with hundred of them, it can make you wait up to 8 minutes!
  • Doing such a simple thing should not require such long documentation. We want tests to be easy and approachable and this doesn’t help.

If you still want to use IdlingResource (c’mon, don’t be so stubborn), see this video:

Better alternative to IdlingResource

*drumrolls*

Write your own checking!

I know, this might sound like much - how you should implement something that took so much code for Android team?

Actually, in many use cases, custom code will be much much smaller. So let’s get back to case of waiting for Activity to be visible.

Whatever way you want to wait for it, you would usually do checking visibility like this:

inline fun <reified T : Activity> isVisible() : Boolean {
    val am = InstrumentationRegistry.getContext().getSystemService(ACTIVITY_SERVICE) as ActivityManager
    val visibleActivityName = am.appTasks[0].taskInfo.baseActivity.className
    return visibleActivityName == T::class.java.name
}

It may seem a bit complicated, but actually you call it like this (thanks to reified): isVisible<MyActivity>() and inside it is checking currently visible activity and comparing if its class name is the same as Activity that you passed. But that’s only condition logic, it can be whatever you want.

Now goes the “harder” part - checking condition. What you really want to do (and how IdlingResource should work IMHO) is just check condition periodically until timeout. So the whole gigantic code looks like this:

val TIMEOUT = 5000L
val CONDITION_CHECK_INTERVAL = 100L

inline fun <reified T : Activity> waitUntilActivityVisible() {
    val startTime = System.currentTimeMillis()
    while (!isVisible<T>()) {
        Thread.sleep(CONDITION_CHECK_INTERVAL)
        if (System.currentTimeMillis() - startTime >= TIMEOUT) {
            throw AssertionError("Activity ${T::class.java.simpleName} not visible after $TIMEOUT milliseconds")
        }
    }
}

That’s all. That’s really everything there is to it. This loop is simple and easy to extend. You don’t have to register it anywhere and unregister it. You just call the method and it blocks until Activity is visible. Just call waitUntilActivityVisible<MyActivity>() and be happy developer!

Alternative

It’s worth noting that there is library (actually, two classes) ConditionWatcher and you can read here how it came to be here (you might have already seen this article, Google places it high with this topic). But beware, some stuff from this article is outdated, e.g.

Furthermore it is IMPORTANT to call there onTransitionToIdle() method ResourceCallback after logical expression starts returning true to notify that change within dynamic resource has occurred.

It’s not true per the latest info on Android Developers website:

public void isIdle() {
    // DON'T call callback.onTransitionToIdle() here!
}

It doesn’t mean that library is bad (it doesn’t use IdlingResource after all), but knowledge from this article should be double checked - apart from this, it’s really good and comprehensive.

Bonus content

Awesome

You know what’s wrong with my code checking condition in loop? Nothing important.

BUT if that was production code and time precision would be important, you should use System.nanoTime() or proven library for measuring time. For more info see Baeldung article.

Just one small bonus tip.