Skip to main content

Discovering Espresso for Android: implementing CountingIdlingResource

Hi, after a long pause I’d like to post an example how to use Espresso’s CountingIdlingResource using lazy getter and setter pattern.

So, at first, what is the  CountingIdlingResource?

CountingIdlingResource - an implementation of IdlingResource that determines idleness by maintaining an internal counter. When the counter is 0 - it is considered to be idle, when it is non-zero it is not idle. This is very similar to the way a java.util.concurrent.Semaphore behaves.

 The counter may be incremented or decremented from any thread. If it reaches an illogical state (like counter less than zero) it will throw an IllegalStateException. This class can then be used to wrap up operations that while in progress should block tests from accessing the UI.

At second, why do I need it? Espresso developers claim that using their test framework you can “Leave your waits, syncs, sleeps, and polls behind and let Espresso gracefully manipulate and assert on the application UI when it is at rest.” This is true, BUT if you are using AsyncTask while performing the network calls.

If  your application under test doesn’t use for some reason AsyncTask and uses ThreadPoolExecutor for network calls. Then this is your case.

It is really pretty easy to register CountingIdlingResource for this thread pool in your test package having getter and setter for the ThreadPoolExecutor instance in main app. The only requirement is to make this instance static to be able to set it from your test package. Below is an example how getter and setter look like in mine code:
public static ThreadPoolExecutor executor;

public void setExecutor(ThreadPoolExecutor executorNew){
    executor = executorNew;
}

public ThreadPoolExecutor getExecutor(){
    if (executor == null){
        executor = new ThreadPoolExecutor(CORE_POOL_SIZE, MAX_POOL_SIZE, 1,
                    TimeUnit.MILLISECONDS, workingQueue);
    }
    return executor;
}
Then in your test package you can do the following :
  1. Create your custom CountingIdlingResource class which extends ThreadPoolExecutor and implements IdlingResource
  2. Override execute(…) method from ThreadPoolExecutor and increase the thread counter every time you hit it
  3. Override afterExecute(…) method to be able to decrease the thread counter when call finished and notify the Espresso when the thread counter is equal to zero that you are onTransitionToIdle state
  4. What I also find useful is to have method in this class that registers CountingIdlingResource for Espresso – registerCounterIdlingResources().
class EspressoCountingIdlingResource extends ThreadPoolExecutor implements IdlingResource {

    IdlingResource.ResourceCallback callback;
    private int threadCount = 0;
    static EspressoCountingIdlingResource idlingResource;
    static PriorityBlockingQueue workingQueue;
    static boolean flag = false;

    public EspressoCountingIdlingResource(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
    }

    @Override
    public synchronized void execute(Runnable r) {
        super.execute(r);
        threadCount++;
    }

    @Override
    protected synchronized void afterExecute(Runnable r, Throwable t) {
        super.afterExecute(r, t);
        threadCount--;
        if (threadCount == 0) {
            if (null != callback) {
                callback.onTransitionToIdle();
            }
        }
    }

    @Override
    public String getName() {
        return "EspressoCountingIdlingResource";
    }

    @Override
    public boolean isIdleNow() {
        return threadCount == 0;
    }

    @Override
    public void registerIdleTransitionCallback(ResourceCallback callback) {
        this.callback = callback;
    }

    public static void registerCounterIdlingResources (){
        if (!flag){
            workingQueue = new PriorityBlockingQueue<>();
            EspressoCountingIdlingResource exec = new EspressoCountingIdlingResource(2, 3, 1, TimeUnit.MILLISECONDS, workingQueue);
            MyCustomPoolRequestHelper poolRequestHelper = new MyCustomPoolRequestHelper();
            poolRequestHelper.setExecutor(exec);
            registerIdlingResources(exec);
            flag= true;
        }
    }
}
After you made all these steps then registering of CountingIdlingResource looks really easy. Register it in your setUp() method like that:
EspressoCountingIdlingResource.registerCounterIdlingResources();
Oh, almost forget small but useful tip – I use the static boolean flag variable to check if  I’ve already register the same CountingIdlingResource for my test suite. If yes then I simply skip it the next time.
As you may know, Espresso can ignore registerIdlingResources() with the same names but in mine setup I don’t want to create once again instances of  MyCustomPoolRequestHelper which is different from that one which was registered at first step and can cause test failure.

Thanks for reading! And feel free to ask questions.

Popular posts from this blog

Espresso & UIAutomator - the perfect tandem

Espresso for Android is perfect and fast test automation framework, but it has one important limitation - you are allowed to operate only inside your app under test context.

That means that it is not possible to automate tests for such app features like:

application push notificationscontact synchronizationnavigating from another app to your app under test,
since you have to deal with other apps from the mobile device - Notification Bar, Contacts or People app, etc. 
In fact it wasn't possible until the release of UIAutomator 2.0. As stated in Android Developers blog post - "...Most importantly, UI Automator is now based on Android Instrumentation...".  And because of that we can run UIAutomator tests as well as Espresso tests using Instrumentation test runner.

In addition to that we can combine UIAutomator tests together with Espresso tests and this gives us the real power and control over the phone and application under test.

In the below example I'll explain  how …

Discovering Espresso for Android: matching and asserting view with text.

After more than a month of using great test tool from Google - Espresso for Android, I'd like to share with you some of my experience. I assume that you've already added espresso jar into your project, spent some time playing with Espresso samples and have basic understanding how this tool works.

In this post I'll show how to match particular view with text or assert that it contains (or not) specified Strings. Before we start, you have to take a look at Hamcrest matchers - Hamcrest tutorial and API Reference Documentation, which are used together with Espresso's ViewAssertions and ViewMatchers and included into Espresso standalone library. Pay more attention to Matcher<java.lang.String> matchers.

So, here we go. For simplicity following String "XXYYZZ" will be used as a expected text pattern.
Espresso ViewMatchers class implements two String matcher methods withText() and withContentDescription() which will match a view which text is equal to specified…

Discovering Espresso for Android: swiping.

Hi, for today I have some tips about swiping ViewActions in Espresso for Android.

As you may know the latest Espresso release contains new swipeLeft and swipeRight ViewActions. They both are really useful when you'd like to swipe between activity fragments, tab layouts or any other UI elements.

You can use it as any other view action:
onView(withId(R.id.viewId)).perform(swipeRight());
But be aware that doing this you will operate on a view, in our case R.id.viewId, but not on the screen size. That means that to swipe right or left, for example between fragments you have to deal with some parent layout or maybe list view.

If you take a look inside Espresso's ViewActions.java class you will see below code for swipeRight() method:
  public static ViewAction swipeRight() {     return new GeneralSwipeAction(Swipe.FAST, GeneralLocation.CENTER_LEFT,         GeneralLocation.CENTER_RIGHT, Press.FINGER);   }
As you may guess GeneralLocation.CENTER_LEFT and GeneralLocation.CENTER_RIGHT…