Showing posts with label idlingresource. Show all posts
Showing posts with label idlingresource. Show all posts

Tuesday, July 28, 2015

Espresso: Wait for dialog to dismiss

Did you complete the Espresso Idling Resource quiz? Here comes the solution!

DialogFragmentIdlingResource

In the quiz, the test fails because we were verifying the text before the loading dialog dismisses. To make it pass, we can add an idling resource that is only idle when the dialog is not shown.

public DialogFragmentIdlingResource(
    FragmentManager manager, String tag) {
  this.manager = manager;
  this.tag = tag;
}

@Override
public boolean isIdleNow() {
  boolean idle = (manager.findFragmentByTag(tag) == null);
  if (idle) {
    resourceCallback.onTransitionToIdle();
  }
  return idle;
}

The idea is to query the FragmentManager with the tag of the loading dialog. If it is null, the DialogFragment is not there, and we consider the app idle.

Using the idling resource in test

@Test
public void done() {
  IdlingResource idlingResource = new DialogFragmentIdlingResource(
      activityRule.getActivity().getSupportFragmentManager(),
      LoadingDialogFragment.TAG);

  Espresso.registerIdlingResources(idlingResource);

  onView(withId(R.id.text))
      .check(matches(withText(R.string.done)));

  Espresso.unregisterIdlingResources(idlingResource);
}

We create an DialogFragmentIdlingResource with the tag from the loading dialog so that it knows what to wait for. With that, the test will wait until the loading dialog dismisses before proceeding. And now the test passes!

Source code

https://github.com/chiuki/espresso-samples/ under idling-resource-dialog-fragment on the solution branch.

Like this article? Take a look at the outline of my Espresso book and fill in this form to push me to write it! Also check out the published courses: https://gumroad.com/chiuki

Tuesday, July 14, 2015

Espresso: Idling Resource quiz

I have written two blog posts on Espresso custom idling resource:

And I am wondering: Is this enough? Do people understand? To figure that out, I made a quiz!

Exercise for the reader

I created a failing test, and your task is to make it pass by adding a custom idling resource. Ready? Here is the code:

https://github.com/chiuki/espresso-samples under idling-resource-dialog-fragment

Fork the repo and commit your answer there. Reply to this Google+ thread with a link to your repo.

The solution will be posted in two weeks.

Solution

Two weeks has passed, so here is the solution!

Like this article? Take a look at the outline of my Espresso book and fill in this form to push me to write it! Also check out the published courses: https://gumroad.com/chiuki

Sunday, June 7, 2015

Espresso: Elapsed time

IdlingResource is a powerful concept in Espresso that is a little difficult to grasp. I have already written an example to wait for an IntentService to be idle, but I thought I'd write another example where it literally waits for time to pass.

Timer game

You start the timer, wait for a minute, and stop it. The game mocks you if you don't wait long enough:

If you wait for longer than a minute, the game congratulates you:

IdlingResource

To test this, we need to ask Espresso to wait. We could use SystemClock.sleep(60000), but that blocks the testing thread. Instead, we write a custom IdlingResource.

public ElapsedTimeIdlingResource(long waitingTime) {
  this.startTime = System.currentTimeMillis();
  this.waitingTime = waitingTime;
}

@Override
public boolean isIdleNow() {
  long elapsed = System.currentTimeMillis() - startTime;
  boolean idle = (elapsed >= waitingTime);
  if (idle) {
    resourceCallback.onTransitionToIdle();
  }
  return idle;
}

In the constructor, we save the current time, and also how long we need to wait. When Espresso wants to know if it should wait more, it calls isIdleNow(), which computes how much time has elapsed, and returns true if it is longer than the amount of time we need to wait.

Timeout policies

Besides the IdlingResource, we also need to change Espresso's timeout policies.

long waitingTime = DateUtils.SECOND_IN_MILLIS * 75;

IdlingPolicies.setMasterPolicyTimeout(
    waitingTime * 2, TimeUnit.MILLISECONDS);
IdlingPolicies.setIdlingResourceTimeout(
    waitingTime * 2, TimeUnit.MILLISECONDS);

IdlingResource idlingResource 
    = new ElapsedTimeIdlingResource(waitingTime);
Espresso.registerIdlingResources(idlingResource);

By default, Espresso times out the app if it has been idle for 60 seconds, and times out an IdlingResource that has been idle for 26 seconds. We change the timeout policies to be twice our waiting time to make sure Espresso does not kill our test.

How does Espresso wait?

If you run this test, you will notice that the IdlingResource does not wait for the exact amount of time you specified, but slightly longer. Espresso waits by periodically checking if everything is idle, and your resource is only idle after the amount of time you specified, so it is guaranteed to wait for at least that long, but it could, and usually does, wait longer.

Full example

Check out the source code for a full example. Pay attention to see how the test cleans up by resetting the timeout policies and unregister the IdlingResource.

Source code: https://github.com/chiuki/espresso-samples/ under idling-resource-elapsed-time

Like this article? Take a look at the outline of my Espresso book and fill in this form to push me to write it! Also check out the published courses: https://gumroad.com/chiuki

Saturday, April 25, 2015

Espresso: Custom Idling Resource

One key feature in Espresso is the synchronization between the test operations and the application being tested. This is built around the concept of idling: Espresso waits until the app is "idle" before performing the next action and checking the next assertion.

Idle

What does it mean for the app to be idle? Espresso checks for several conditions:

  • There is no UI events in the current message queue
  • There is no tasks in the default AsyncTask thread pool

This takes care of waiting for the UI rendering and AsyncTask completion. However, if your app performs long-running operations in other ways, Espresso will not know how to wait for those operations to finish. If that is the case, you can tell Espresso to wait by writing a custom IdlingResource.

IntentServiceIdlingResource

Say you use an IntentService to do some long computation, and return the result to your activity via broadcast. We want Espresso to wait until the result is returned before checking that it was displayed correctly.

To implement an IdlingResource, you need to override 3 functions: getName(), registerIdleTransitionCallback() and isIdleNow().

@Override
public String getName() {
  return IntentServiceIdlingResource.class.getName();
}

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

@Override
public boolean isIdleNow() {
  boolean idle = !isIntentServiceRunning();
  if (idle && resourceCallback != null) {
    resourceCallback.onTransitionToIdle();
  }
  return idle;
}

private boolean isIntentServiceRunning() {
  ActivityManager manager = 
    (ActivityManager) context.getSystemService(
      Context.ACTIVITY_SERVICE);
  for (ActivityManager.RunningServiceInfo info : 
          manager.getRunningServices(Integer.MAX_VALUE)) {
    if (RepeatService.class.getName().equals(
          info.service.getClassName())) {
      return true;
    }
  }
  return false;
}

The idle logic is implemented in isIdleNow(). In our case, we check if our IntentService is running by querying the ActivityManager. If the IntentService is not running, we can inform Espresso calling resourceCallback.onTransitionToIdle().

Register your idling resource

You need to register your custom idling resource in order for Espresso to wait for it. Do it in a @Before method in your test, and unregister it in @After.

@Before
public void registerIntentServiceIdlingResource() {
  Instrumentation instrumentation 
    = InstrumentationRegistry.getInstrumentation();
  idlingResource = new IntentServiceIdlingResource(
    instrumentation.getTargetContext());
  Espresso.registerIdlingResources(idlingResource);
}

@After
public void unregisterIntentServiceIdlingResource() {
  Espresso.unregisterIdlingResources(idlingResource);
}

Full example

Check out the source code for a full example. Try commenting out the IdlingResource registration and watch the test fail.

Source code: https://github.com/chiuki/espresso-samples/ under idling-resource-intent-service

Like this article? Take a look at the outline of my Espresso book and fill in this form to push me to write it! Also check out the published courses: https://gumroad.com/chiuki